diff --git a/runtime/tests/vm/dart/inline_stack_frame_test.dart b/runtime/tests/vm/dart/inline_stack_frame_test.dart index e5538553ebe..d39795715b4 100644 --- a/runtime/tests/vm/dart/inline_stack_frame_test.dart +++ b/runtime/tests/vm/dart/inline_stack_frame_test.dart @@ -4,8 +4,6 @@ // VMOptions=--optimization_counter_threshold=10 --no-background-compilation -import "package:expect/expect.dart"; - // This test tries to verify that we produce the correct stack trace when // throwing exceptions even when functions are inlined. // The test invokes a bunch of functions and then does a throw. There is a @@ -26,7 +24,7 @@ class Test { } return ""; } catch (e, stacktrace) { - var result = e + stacktrace.toString(); + var result = e + "\n" + stacktrace.toString(); return result; } } @@ -57,22 +55,33 @@ class Test { } } +expectHasSubstring(String string, String substring) { + if (!string.contains(substring)) { + var sb = new StringBuffer(); + sb.writeln("Expect string:"); + sb.writeln(string); + sb.writeln("To have substring:"); + sb.writeln(substring); + throw new Exception(sb.toString()); + } +} + main() { var x = new Test(); var result = x.func1(100000); - Expect.isTrue(result.contains("show me inlined functions")); - Expect.isTrue(result.contains("Test.func1")); - Expect.isTrue(result.contains("Test.func2")); - Expect.isTrue(result.contains("Test.func3")); - Expect.isTrue(result.contains("Test.func4")); - Expect.isTrue(result.contains("Test.func")); + expectHasSubstring(result, "show me inlined functions"); + expectHasSubstring(result, "Test.func1"); + expectHasSubstring(result, "Test.func2"); + expectHasSubstring(result, "Test.func3"); + expectHasSubstring(result, "Test.func4"); + expectHasSubstring(result, "Test.func5"); for (var i = 0; i <= 10; i++) { result = x.func1(i); } - Expect.isTrue(result.contains("show me inlined functions")); - Expect.isTrue(result.contains("Test.func1")); - Expect.isTrue(result.contains("Test.func2")); - Expect.isTrue(result.contains("Test.func3")); - Expect.isTrue(result.contains("Test.func4")); - Expect.isTrue(result.contains("Test.func5")); + expectHasSubstring(result, "show me inlined functions"); + expectHasSubstring(result, "Test.func1"); + expectHasSubstring(result, "Test.func2"); + expectHasSubstring(result, "Test.func3"); + expectHasSubstring(result, "Test.func4"); + expectHasSubstring(result, "Test.func5"); } diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc index 162df5aba3a..7c7a061b631 100644 --- a/runtime/vm/clustered_snapshot.cc +++ b/runtime/vm/clustered_snapshot.cc @@ -1571,7 +1571,8 @@ class CodeSerializationCluster : public SerializationCluster { if (s->kind() == Snapshot::kAppJIT) { s->Push(code->ptr()->deopt_info_array_); s->Push(code->ptr()->static_calls_target_table_); - NOT_IN_PRODUCT(s->Push(code->ptr()->inlined_id_to_function_)); + s->Push(code->ptr()->inlined_id_to_function_); + s->Push(code->ptr()->code_source_map_); NOT_IN_PRODUCT(s->Push(code->ptr()->return_address_metadata_)); } } @@ -1624,7 +1625,8 @@ class CodeSerializationCluster : public SerializationCluster { if (s->kind() == Snapshot::kAppJIT) { s->WriteRef(code->ptr()->deopt_info_array_); s->WriteRef(code->ptr()->static_calls_target_table_); - NOT_IN_PRODUCT(s->WriteRef(code->ptr()->inlined_id_to_function_)); + s->WriteRef(code->ptr()->inlined_id_to_function_); + s->WriteRef(code->ptr()->code_source_map_); NOT_IN_PRODUCT(s->WriteRef(code->ptr()->return_address_metadata_)); } @@ -1696,23 +1698,24 @@ class CodeDeserializationCluster : public DeserializationCluster { reinterpret_cast(d->ReadRef()); code->ptr()->static_calls_target_table_ = reinterpret_cast(d->ReadRef()); -#if defined(PRODUCT) - code->ptr()->inlined_id_to_function_ = Array::null(); - code->ptr()->return_address_metadata_ = Object::null(); -#else code->ptr()->inlined_id_to_function_ = reinterpret_cast(d->ReadRef()); + code->ptr()->code_source_map_ = + reinterpret_cast(d->ReadRef()); +#if defined(PRODUCT) + code->ptr()->return_address_metadata_ = Object::null(); +#else code->ptr()->return_address_metadata_ = d->ReadRef(); #endif } else { code->ptr()->deopt_info_array_ = Array::null(); code->ptr()->static_calls_target_table_ = Array::null(); code->ptr()->inlined_id_to_function_ = Array::null(); + code->ptr()->code_source_map_ = CodeSourceMap::null(); code->ptr()->return_address_metadata_ = Object::null(); } code->ptr()->var_descriptors_ = LocalVarDescriptors::null(); - code->ptr()->code_source_map_ = CodeSourceMap::null(); code->ptr()->comments_ = Array::null(); code->ptr()->compile_timestamp_ = 0; @@ -4545,6 +4548,8 @@ SerializationCluster* Serializer::NewClusterForClass(intptr_t cid) { return new (Z) ObjectPoolSerializationCluster(); case kPcDescriptorsCid: return new (Z) RODataSerializationCluster(kPcDescriptorsCid); + case kCodeSourceMapCid: + return new (Z) RODataSerializationCluster(kCodeSourceMapCid); case kStackMapCid: return new (Z) RODataSerializationCluster(kStackMapCid); case kExceptionHandlersCid: @@ -4910,6 +4915,7 @@ DeserializationCluster* Deserializer::ReadCluster() { case kObjectPoolCid: return new (Z) ObjectPoolDeserializationCluster(); case kPcDescriptorsCid: + case kCodeSourceMapCid: case kStackMapCid: return new (Z) RODataDeserializationCluster(); case kExceptionHandlersCid: diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc index 0631bfee3f5..7bccb7d2e3e 100644 --- a/runtime/vm/code_descriptors.cc +++ b/runtime/vm/code_descriptors.cc @@ -128,26 +128,86 @@ const TokenPosition CodeSourceMapBuilder::kInitialPosition = CodeSourceMapBuilder::CodeSourceMapBuilder( + bool stack_traces_only, const GrowableArray& caller_inline_id, const GrowableArray& inline_id_to_token_pos, const GrowableArray& inline_id_to_function) - : pc_offset_(0), - advance_pc_peephole_(0), - inline_id_stack_(), - token_pos_stack_(), + : buffered_pc_offset_(0), + buffered_inline_id_stack_(), + buffered_token_pos_stack_(), + written_pc_offset_(0), + written_inline_id_stack_(), + written_token_pos_stack_(), caller_inline_id_(caller_inline_id), inline_id_to_token_pos_(inline_id_to_token_pos), inline_id_to_function_(inline_id_to_function), buffer_(NULL), - stream_(&buffer_, zone_allocator, 64) { - inline_id_stack_.Add(0); - token_pos_stack_.Add(TokenPosition::kDartCodePrologue); + stream_(&buffer_, zone_allocator, 64), + stack_traces_only_(stack_traces_only) { + buffered_inline_id_stack_.Add(0); + buffered_token_pos_stack_.Add(kInitialPosition); + written_inline_id_stack_.Add(0); + written_token_pos_stack_.Add(kInitialPosition); +} + + +void CodeSourceMapBuilder::FlushBuffer() { + FlushBufferStack(); + FlushBufferPosition(); + FlushBufferPC(); +} + + +void CodeSourceMapBuilder::FlushBufferStack() { + for (intptr_t i = buffered_inline_id_stack_.length() - 1; i >= 0; i--) { + intptr_t buffered_id = buffered_inline_id_stack_[i]; + if (i < written_inline_id_stack_.length()) { + intptr_t written_id = written_inline_id_stack_[i]; + if (buffered_id == written_id) { + // i is the top-most position where the buffered and written stack + // match. + while (written_inline_id_stack_.length() > i + 1) { + WritePop(); + } + for (intptr_t j = i + 1; j < buffered_inline_id_stack_.length(); j++) { + TokenPosition buffered_pos = buffered_token_pos_stack_[j - 1]; + TokenPosition written_pos = written_token_pos_stack_[j - 1]; + if (buffered_pos != written_pos) { + WriteChangePosition(buffered_pos); + } + WritePush(buffered_inline_id_stack_[j]); + } + return; + } + } + } + UNREACHABLE(); +} + + +void CodeSourceMapBuilder::FlushBufferPosition() { + ASSERT(buffered_token_pos_stack_.length() == + written_token_pos_stack_.length()); + + intptr_t top = buffered_token_pos_stack_.length() - 1; + TokenPosition buffered_pos = buffered_token_pos_stack_[top]; + TokenPosition written_pos = written_token_pos_stack_[top]; + if (buffered_pos != written_pos) { + WriteChangePosition(buffered_pos); + } +} + + +void CodeSourceMapBuilder::FlushBufferPC() { + if (buffered_pc_offset_ != written_pc_offset_) { + WriteAdvancePC(buffered_pc_offset_ - written_pc_offset_); + } } void CodeSourceMapBuilder::StartInliningInterval(int32_t pc_offset, intptr_t inline_id) { - if (inline_id_stack_.Last() == inline_id) { + if (buffered_inline_id_stack_.Last() == inline_id) { // No change in function stack. return; } @@ -156,18 +216,20 @@ void CodeSourceMapBuilder::StartInliningInterval(int32_t pc_offset, return; } + if (!stack_traces_only_) { + FlushBuffer(); + } + // Find a minimal set of pops and pushes to bring us to the new function // stack. // Pop to a common ancestor. intptr_t common_parent = inline_id; - while (!IsOnStack(common_parent)) { + while (!IsOnBufferedStack(common_parent)) { common_parent = caller_inline_id_[common_parent]; } - while (inline_id_stack_.Last() != common_parent) { - EmitPop(); - inline_id_stack_.RemoveLast(); - token_pos_stack_.RemoveLast(); + while (buffered_inline_id_stack_.Last() != common_parent) { + BufferPop(); } // Push to the new top-of-stack function. @@ -178,23 +240,19 @@ void CodeSourceMapBuilder::StartInliningInterval(int32_t pc_offset, id = caller_inline_id_[id]; } for (intptr_t i = to_push.length() - 1; i >= 0; i--) { - intptr_t push_id = to_push[i]; + intptr_t callee_id = to_push[i]; TokenPosition call_token; - if (push_id != 0) { + if (callee_id != 0) { // TODO(rmacnak): Should make this array line up with the others. - call_token = inline_id_to_token_pos_[push_id - 1]; + call_token = inline_id_to_token_pos_[callee_id - 1]; + } else { + UNREACHABLE(); } // Report caller as at the position of the call. - if (call_token != token_pos_stack_.Last()) { - EmitPosition(call_token); - token_pos_stack_[token_pos_stack_.length() - 1] = call_token; - } + BufferChangePosition(call_token); - // Push the callee. - EmitPush(push_id); - inline_id_stack_.Add(push_id); - token_pos_stack_.Add(TokenPosition::kDartCodePrologue); + BufferPush(callee_id); } } @@ -204,15 +262,30 @@ void CodeSourceMapBuilder::BeginCodeSourceRange(int32_t pc_offset) {} void CodeSourceMapBuilder::EndCodeSourceRange(int32_t pc_offset, TokenPosition pos) { - if (pc_offset == pc_offset_) { + if (pc_offset == buffered_pc_offset_) { return; // Empty intermediate instruction. } - if (pos != token_pos_stack_.Last()) { - EmitPosition(pos); - token_pos_stack_[token_pos_stack_.length() - 1] = pos; + if (pos != buffered_token_pos_stack_.Last()) { + if (!stack_traces_only_) { + FlushBuffer(); + } + BufferChangePosition(pos); + } + BufferAdvancePC(pc_offset - buffered_pc_offset_); +} + + +void CodeSourceMapBuilder::NoteDescriptor(RawPcDescriptors::Kind kind, + int32_t pc_offset, + TokenPosition pos) { + const uint8_t kCanThrow = + RawPcDescriptors::kIcCall | RawPcDescriptors::kUnoptStaticCall | + RawPcDescriptors::kRuntimeCall | RawPcDescriptors::kOther; + if (stack_traces_only_ && ((kind & kCanThrow) != 0)) { + BufferChangePosition(pos); + BufferAdvancePC(pc_offset - buffered_pc_offset_); + FlushBuffer(); } - EmitAdvancePC(pc_offset - pc_offset_); - pc_offset_ = pc_offset; } @@ -231,7 +304,9 @@ RawArray* CodeSourceMapBuilder::InliningIdToFunction() { RawCodeSourceMap* CodeSourceMapBuilder::Finalize() { - FlushPeephole(); + if (!stack_traces_only_) { + FlushBuffer(); + } intptr_t length = stream_.bytes_written(); const CodeSourceMap& map = CodeSourceMap::Handle(CodeSourceMap::New(length)); NoSafepointScope no_safepoint; diff --git a/runtime/vm/code_descriptors.h b/runtime/vm/code_descriptors.h index d2faea285db..cde1cfce8e1 100644 --- a/runtime/vm/code_descriptors.h +++ b/runtime/vm/code_descriptors.h @@ -10,6 +10,7 @@ #include "vm/globals.h" #include "vm/growable_array.h" #include "vm/object.h" +#include "vm/log.h" namespace dart { @@ -139,9 +140,20 @@ class ExceptionHandlerList : public ZoneAllocated { }; +// A CodeSourceMap maps from pc offsets to a stack of inlined functions and +// their positions. This is encoded as a little bytecode that pushes and pops +// functions and changes the top function's position as the PC advances. +// Decoding happens by running this bytecode until we reach the desired PC. +// +// The implementation keeps track of two sets of state: one written to the byte +// stream and one that is buffered. On the JIT, this buffering effectively gives +// us a peephole optimization that merges adjacent advance PC bytecodes. On AOT, +// this allows to skip encoding our position until we reach a PC where we might +// throw. class CodeSourceMapBuilder : public ZoneAllocated { public: CodeSourceMapBuilder( + bool stack_traces_only, const GrowableArray& caller_inline_id, const GrowableArray& inline_id_to_token_pos, const GrowableArray& inline_id_to_function); @@ -159,47 +171,67 @@ class CodeSourceMapBuilder : public ZoneAllocated { void StartInliningInterval(int32_t pc_offset, intptr_t inline_id); void BeginCodeSourceRange(int32_t pc_offset); void EndCodeSourceRange(int32_t pc_offset, TokenPosition pos); + void NoteDescriptor(RawPcDescriptors::Kind kind, + int32_t pc_offset, + TokenPosition pos); RawArray* InliningIdToFunction(); RawCodeSourceMap* Finalize(); private: - void EmitPosition(TokenPosition pos) { - FlushPeephole(); + void BufferChangePosition(TokenPosition pos) { + buffered_token_pos_stack_.Last() = pos; + } + void WriteChangePosition(TokenPosition pos) { stream_.Write(kChangePosition); stream_.Write(static_cast(pos.value())); + written_token_pos_stack_.Last() = pos; } - void EmitAdvancePC(int32_t distance) { advance_pc_peephole_ += distance; } - void FlushPeephole() { - if (advance_pc_peephole_ != 0) { - stream_.Write(kAdvancePC); - stream_.Write(advance_pc_peephole_); - advance_pc_peephole_ = 0; - } + void BufferAdvancePC(int32_t distance) { buffered_pc_offset_ += distance; } + void WriteAdvancePC(int32_t distance) { + stream_.Write(kAdvancePC); + stream_.Write(distance); + written_pc_offset_ += distance; } - void EmitPush(intptr_t inline_id) { - FlushPeephole(); + void BufferPush(intptr_t inline_id) { + buffered_inline_id_stack_.Add(inline_id); + buffered_token_pos_stack_.Add(kInitialPosition); + } + void WritePush(intptr_t inline_id) { stream_.Write(kPushFunction); stream_.Write(inline_id); + written_inline_id_stack_.Add(inline_id); + written_token_pos_stack_.Add(kInitialPosition); } - void EmitPop() { - FlushPeephole(); + void BufferPop() { + buffered_inline_id_stack_.RemoveLast(); + buffered_token_pos_stack_.RemoveLast(); + } + void WritePop() { stream_.Write(kPopFunction); + written_inline_id_stack_.RemoveLast(); + written_token_pos_stack_.RemoveLast(); } - bool IsOnStack(intptr_t inline_id) { - for (intptr_t i = 0; i < inline_id_stack_.length(); i++) { - if (inline_id_stack_[i] == inline_id) { - return true; - } + void FlushBuffer(); + void FlushBufferStack(); + void FlushBufferPosition(); + void FlushBufferPC(); + + bool IsOnBufferedStack(intptr_t inline_id) { + for (intptr_t i = 0; i < buffered_inline_id_stack_.length(); i++) { + if (buffered_inline_id_stack_[i] == inline_id) return true; } return false; } - intptr_t pc_offset_; - intptr_t advance_pc_peephole_; - GrowableArray inline_id_stack_; - GrowableArray token_pos_stack_; + intptr_t buffered_pc_offset_; + GrowableArray buffered_inline_id_stack_; + GrowableArray buffered_token_pos_stack_; + + intptr_t written_pc_offset_; + GrowableArray written_inline_id_stack_; + GrowableArray written_token_pos_stack_; const GrowableArray& caller_inline_id_; const GrowableArray& inline_id_to_token_pos_; @@ -208,6 +240,8 @@ class CodeSourceMapBuilder : public ZoneAllocated { uint8_t* buffer_; WriteStream stream_; + const bool stack_traces_only_; + DISALLOW_COPY_AND_ASSIGN(CodeSourceMapBuilder); }; diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index 4b145ad2724..37ce72e9522 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -1578,35 +1578,41 @@ DebuggerStackTrace* Debugger::StackTraceFrom(const class StackTrace& ex_trace) { const intptr_t deopt_frame_offset = -1; for (intptr_t i = 0; i < ex_trace.Length(); i++) { - function = ex_trace.FunctionAtFrame(i); + code = ex_trace.CodeAtFrame(i); // Pre-allocated StackTraces may include empty slots, either (a) to indicate // where frames were omitted in the case a stack has more frames than the // pre-allocated trace (such as a stack overflow) or (b) because a stack has // fewer frames that the pre-allocated trace (such as memory exhaustion with // a shallow stack). - if (!function.IsNull() && function.is_visible()) { - code = ex_trace.CodeAtFrame(i); - ASSERT(function.raw() == code.function()); - uword pc = code.PayloadStart() + Smi::Value(ex_trace.PcOffsetAtFrame(i)); - if (code.is_optimized() && ex_trace.expand_inlined()) { - // Traverse inlined frames. - for (InlinedFunctionsIterator it(code, pc); !it.Done(); it.Advance()) { - function = it.function(); - code = it.code(); - ASSERT(function.raw() == code.function()); - uword pc = it.pc(); - ASSERT(pc != 0); - ASSERT(code.PayloadStart() <= pc); - ASSERT(pc < (code.PayloadStart() + code.Size())); + if (!code.IsNull()) { + ASSERT(code.IsFunctionCode()); + function = code.function(); + if (function.is_visible()) { + code = ex_trace.CodeAtFrame(i); + ASSERT(function.raw() == code.function()); + uword pc = + code.PayloadStart() + Smi::Value(ex_trace.PcOffsetAtFrame(i)); + if (code.is_optimized() && ex_trace.expand_inlined()) { + // Traverse inlined frames. + for (InlinedFunctionsIterator it(code, pc); !it.Done(); + it.Advance()) { + function = it.function(); + code = it.code(); + ASSERT(function.raw() == code.function()); + uword pc = it.pc(); + ASSERT(pc != 0); + ASSERT(code.PayloadStart() <= pc); + ASSERT(pc < (code.PayloadStart() + code.Size())); + ActivationFrame* activation = new ActivationFrame( + pc, fp, sp, code, deopt_frame, deopt_frame_offset); + stack_trace->AddActivation(activation); + } + } else { ActivationFrame* activation = new ActivationFrame( pc, fp, sp, code, deopt_frame, deopt_frame_offset); stack_trace->AddActivation(activation); } - } else { - ActivationFrame* activation = new ActivationFrame( - pc, fp, sp, code, deopt_frame, deopt_frame_offset); - stack_trace->AddActivation(activation); } } } diff --git a/runtime/vm/disassembler.cc b/runtime/vm/disassembler.cc index 142f42ee8ed..c51f6e4be3b 100644 --- a/runtime/vm/disassembler.cc +++ b/runtime/vm/disassembler.cc @@ -129,7 +129,8 @@ void Disassembler::Disassemble(uword start, char str[4000]; BufferFormatter f(str, sizeof(str)); // Comment emitted, emit inlining information. - code.GetInlinedFunctionsAt(offset, &inlined_functions, &token_positions); + code.GetInlinedFunctionsAtInstruction(offset, &inlined_functions, + &token_positions); // Skip top scope function printing (last entry in 'inlined_functions'). bool first = true; for (intptr_t i = 1; i < inlined_functions.length(); i++) { diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc index bc0bd111aee..8da9f575110 100644 --- a/runtime/vm/exceptions.cc +++ b/runtime/vm/exceptions.cc @@ -98,7 +98,7 @@ void PreallocatedStackTraceBuilder::AddFrame(const Code& code, dropped_frames_++; // Add an empty slot to indicate the overflow so that the toString // method can account for the overflow. - if (stacktrace_.FunctionAtFrame(null_slot) != Function::null()) { + if (stacktrace_.CodeAtFrame(null_slot) != Code::null()) { stacktrace_.SetCodeAtFrame(null_slot, frame_code); // We drop an extra frame here too. dropped_frames_++; diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc index b23b034049f..f54e104845f 100644 --- a/runtime/vm/flow_graph_compiler.cc +++ b/runtime/vm/flow_graph_compiler.cc @@ -240,8 +240,10 @@ FlowGraphCompiler::FlowGraphCompiler( ASSERT(assembler != NULL); ASSERT(!list_class_.IsNull()); - code_source_map_builder_ = new (zone_) CodeSourceMapBuilder( - caller_inline_id, inline_id_to_token_pos, inline_id_to_function); + bool stack_traces_only = !FLAG_profiler; + code_source_map_builder_ = new (zone_) + CodeSourceMapBuilder(stack_traces_only, caller_inline_id, + inline_id_to_token_pos, inline_id_to_function); } @@ -681,14 +683,25 @@ void FlowGraphCompiler::SetNeedsStackTrace(intptr_t try_index) { } +void FlowGraphCompiler::AddDescriptor(RawPcDescriptors::Kind kind, + intptr_t pc_offset, + intptr_t deopt_id, + TokenPosition token_pos, + intptr_t try_index) { + code_source_map_builder_->NoteDescriptor(kind, pc_offset, token_pos); + // When running with optimizations disabled, don't emit deopt-descriptors. + if (!CanOptimize() && (kind == RawPcDescriptors::kDeopt)) return; + pc_descriptors_list_->AddDescriptor(kind, pc_offset, deopt_id, token_pos, + try_index); +} + + // Uses current pc position and try-index. void FlowGraphCompiler::AddCurrentDescriptor(RawPcDescriptors::Kind kind, intptr_t deopt_id, TokenPosition token_pos) { - // When running with optimizations disabled, don't emit deopt-descriptors. - if (!CanOptimize() && (kind == RawPcDescriptors::kDeopt)) return; - pc_descriptors_list()->AddDescriptor(kind, assembler()->CodeSize(), deopt_id, - token_pos, CurrentTryIndex()); + AddDescriptor(kind, assembler()->CodeSize(), deopt_id, token_pos, + CurrentTryIndex()); } @@ -1027,9 +1040,6 @@ void FlowGraphCompiler::FinalizeStaticCallTargetsTable(const Code& code) { void FlowGraphCompiler::FinalizeCodeSourceMap(const Code& code) { -#ifdef PRODUCT -// This data is only used by the profiler. -#else if (FLAG_precompiled_mode) { // TODO(rmacnak): Include a filtered verion of this to produce stack traces // with inlined frames. @@ -1046,7 +1056,6 @@ void FlowGraphCompiler::FinalizeCodeSourceMap(const Code& code) { CodeSourceMap::Handle(code_source_map_builder_->Finalize()); INC_STAT(Thread::Current(), total_code_size, map.Length() * sizeof(uint8_t)); code.set_code_source_map(map); -#endif #if defined(DEBUG) // Force simulation through the last pc offset. This checks we can decode @@ -1054,7 +1063,7 @@ void FlowGraphCompiler::FinalizeCodeSourceMap(const Code& code) { // etc. GrowableArray fs; GrowableArray tokens; - code.GetInlinedFunctionsAt(code.Size() - 1, &fs, &tokens); + code.GetInlinedFunctionsAtInstruction(code.Size() - 1, &fs, &tokens); #endif } diff --git a/runtime/vm/flow_graph_compiler.h b/runtime/vm/flow_graph_compiler.h index 3d8196a8d53..d51002f58e1 100644 --- a/runtime/vm/flow_graph_compiler.h +++ b/runtime/vm/flow_graph_compiler.h @@ -307,7 +307,6 @@ class FlowGraphCompiler : public ValueObject { const FlowGraph& flow_graph() const { return flow_graph_; } - DescriptorList* pc_descriptors_list() const { return pc_descriptors_list_; } BlockEntryInstr* current_block() const { return current_block_; } void set_current_block(BlockEntryInstr* value) { current_block_ = value; } static bool CanOptimize(); @@ -498,6 +497,11 @@ class FlowGraphCompiler : public ValueObject { void AddCurrentDescriptor(RawPcDescriptors::Kind kind, intptr_t deopt_id, TokenPosition token_pos); + void AddDescriptor(RawPcDescriptors::Kind kind, + intptr_t pc_offset, + intptr_t deopt_id, + TokenPosition token_pos, + intptr_t try_index); void RecordSafepoint(LocationSummary* locs, intptr_t slow_path_argument_count = 0); diff --git a/runtime/vm/flow_graph_compiler_arm.cc b/runtime/vm/flow_graph_compiler_arm.cc index e16256c0d09..700935b5196 100644 --- a/runtime/vm/flow_graph_compiler_arm.cc +++ b/runtime/vm/flow_graph_compiler_arm.cc @@ -1282,9 +1282,8 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall( if (try_index == CatchClauseNode::kInvalidTryIndex) { try_index = CurrentTryIndex(); } - pc_descriptors_list()->AddDescriptor( - RawPcDescriptors::kOther, assembler()->CodeSize(), Thread::kNoDeoptId, - token_pos, try_index); + AddDescriptor(RawPcDescriptors::kOther, assembler()->CodeSize(), + Thread::kNoDeoptId, token_pos, try_index); } else if (is_optimizing()) { AddCurrentDescriptor(RawPcDescriptors::kOther, Thread::kNoDeoptId, token_pos); diff --git a/runtime/vm/flow_graph_compiler_arm64.cc b/runtime/vm/flow_graph_compiler_arm64.cc index 8534b1f729d..d320028ed28 100644 --- a/runtime/vm/flow_graph_compiler_arm64.cc +++ b/runtime/vm/flow_graph_compiler_arm64.cc @@ -1270,9 +1270,8 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall( if (try_index == CatchClauseNode::kInvalidTryIndex) { try_index = CurrentTryIndex(); } - pc_descriptors_list()->AddDescriptor( - RawPcDescriptors::kOther, assembler()->CodeSize(), Thread::kNoDeoptId, - token_pos, try_index); + AddDescriptor(RawPcDescriptors::kOther, assembler()->CodeSize(), + Thread::kNoDeoptId, token_pos, try_index); } else if (is_optimizing()) { AddCurrentDescriptor(RawPcDescriptors::kOther, Thread::kNoDeoptId, token_pos); diff --git a/runtime/vm/flow_graph_compiler_mips.cc b/runtime/vm/flow_graph_compiler_mips.cc index b6914ad6b2e..7854b08c431 100644 --- a/runtime/vm/flow_graph_compiler_mips.cc +++ b/runtime/vm/flow_graph_compiler_mips.cc @@ -1287,9 +1287,8 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall( if (try_index == CatchClauseNode::kInvalidTryIndex) { try_index = CurrentTryIndex(); } - pc_descriptors_list()->AddDescriptor( - RawPcDescriptors::kOther, assembler()->CodeSize(), Thread::kNoDeoptId, - token_pos, try_index); + AddDescriptor(RawPcDescriptors::kOther, assembler()->CodeSize(), + Thread::kNoDeoptId, token_pos, try_index); } else if (is_optimizing()) { AddCurrentDescriptor(RawPcDescriptors::kOther, Thread::kNoDeoptId, token_pos); diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc index fa35f471060..6c69c8062be 100644 --- a/runtime/vm/flow_graph_compiler_x64.cc +++ b/runtime/vm/flow_graph_compiler_x64.cc @@ -1279,9 +1279,8 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall( if (try_index == CatchClauseNode::kInvalidTryIndex) { try_index = CurrentTryIndex(); } - pc_descriptors_list()->AddDescriptor( - RawPcDescriptors::kOther, assembler()->CodeSize(), Thread::kNoDeoptId, - token_pos, try_index); + AddDescriptor(RawPcDescriptors::kOther, assembler()->CodeSize(), + Thread::kNoDeoptId, token_pos, try_index); } else if (is_optimizing()) { AddCurrentDescriptor(RawPcDescriptors::kOther, Thread::kNoDeoptId, token_pos); diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc index afe7d115c3c..093970f4e3e 100644 --- a/runtime/vm/intermediate_language_arm.cc +++ b/runtime/vm/intermediate_language_arm.cc @@ -6400,7 +6400,7 @@ class RangeErrorSlowPath : public SlowPathCode { __ Push(locs->in(0).reg()); __ Push(locs->in(1).reg()); __ CallRuntime(kRangeErrorRuntimeEntry, 2); - compiler->pc_descriptors_list()->AddDescriptor( + compiler->AddDescriptor( RawPcDescriptors::kOther, compiler->assembler()->CodeSize(), instruction_->deopt_id(), instruction_->token_pos(), try_index_); compiler->RecordSafepoint(locs, 2); diff --git a/runtime/vm/intermediate_language_arm64.cc b/runtime/vm/intermediate_language_arm64.cc index 006dd3be46c..fa44b58f1d9 100644 --- a/runtime/vm/intermediate_language_arm64.cc +++ b/runtime/vm/intermediate_language_arm64.cc @@ -5547,7 +5547,7 @@ class RangeErrorSlowPath : public SlowPathCode { __ Push(locs->in(0).reg()); __ Push(locs->in(1).reg()); __ CallRuntime(kRangeErrorRuntimeEntry, 2); - compiler->pc_descriptors_list()->AddDescriptor( + compiler->AddDescriptor( RawPcDescriptors::kOther, compiler->assembler()->CodeSize(), instruction_->deopt_id(), instruction_->token_pos(), try_index_); compiler->RecordSafepoint(locs, 2); diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc index 5494f62bad6..fec6937e100 100644 --- a/runtime/vm/intermediate_language_mips.cc +++ b/runtime/vm/intermediate_language_mips.cc @@ -5132,7 +5132,7 @@ class RangeErrorSlowPath : public SlowPathCode { __ Push(locs->in(0).reg()); __ Push(locs->in(1).reg()); __ CallRuntime(kRangeErrorRuntimeEntry, 2); - compiler->pc_descriptors_list()->AddDescriptor( + compiler->AddDescriptor( RawPcDescriptors::kOther, compiler->assembler()->CodeSize(), instruction_->deopt_id(), instruction_->token_pos(), try_index_); __ break_(0); diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc index 7cebfd70433..a835e4c7041 100644 --- a/runtime/vm/intermediate_language_x64.cc +++ b/runtime/vm/intermediate_language_x64.cc @@ -5908,7 +5908,7 @@ class RangeErrorSlowPath : public SlowPathCode { __ pushq(locs->in(0).reg()); __ pushq(locs->in(1).reg()); __ CallRuntime(kRangeErrorRuntimeEntry, 2); - compiler->pc_descriptors_list()->AddDescriptor( + compiler->AddDescriptor( RawPcDescriptors::kOther, compiler->assembler()->CodeSize(), instruction_->deopt_id(), instruction_->token_pos(), try_index_); compiler->RecordSafepoint(locs, 2); diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 8df9f9333e0..d48e22f0eab 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -14460,7 +14460,7 @@ RawStackMap* Code::GetStackMap(uint32_t pc_offset, } -void Code::GetInlinedFunctionsAt( +void Code::GetInlinedFunctionsAtInstruction( intptr_t pc_offset, GrowableArray* functions, GrowableArray* token_positions) const { @@ -22263,12 +22263,6 @@ intptr_t StackTrace::Length() const { } -RawFunction* StackTrace::FunctionAtFrame(intptr_t frame_index) const { - const Code& code = Code::Handle(CodeAtFrame(frame_index)); - return code.IsNull() ? Function::null() : code.function(); -} - - RawCode* StackTrace::CodeAtFrame(intptr_t frame_index) const { const Array& code_array = Array::Handle(raw_ptr()->code_array_); return reinterpret_cast(code_array.At(frame_index)); @@ -22337,13 +22331,11 @@ const char* StackTrace::ToCString() const { } -static intptr_t PrintOneStackTrace(Zone* zone, - GrowableArray* frame_strings, - uword pc, - const Function& function, - const Code& code, - intptr_t frame_index) { - const TokenPosition token_pos = code.GetTokenIndexOfPC(pc); +static void PrintStackTraceFrame(Zone* zone, + TextBuffer* buffer, + const Function& function, + TokenPosition token_pos, + intptr_t frame_index) { const Script& script = Script::Handle(zone, function.script()); const String& function_name = String::Handle(zone, function.QualifiedUserVisibleName()); @@ -22358,90 +22350,73 @@ static intptr_t PrintOneStackTrace(Zone* zone, script.GetTokenLocation(token_pos, &line, NULL); } } - char* chars = NULL; if (column >= 0) { - chars = - OS::SCreate(zone, "#%-6" Pd " %s (%s:%" Pd ":%" Pd ")\n", frame_index, - function_name.ToCString(), url.ToCString(), line, column); + buffer->Printf("#%-6" Pd " %s (%s:%" Pd ":%" Pd ")\n", frame_index, + function_name.ToCString(), url.ToCString(), line, column); } else if (line >= 0) { - chars = OS::SCreate(zone, "#%-6" Pd " %s (%s:%" Pd ")\n", frame_index, - function_name.ToCString(), url.ToCString(), line); + buffer->Printf("#%-6" Pd " %s (%s:%" Pd ")\n", frame_index, + function_name.ToCString(), url.ToCString(), line); } else { - chars = OS::SCreate(zone, "#%-6" Pd " %s (%s)\n", frame_index, - function_name.ToCString(), url.ToCString()); + buffer->Printf("#%-6" Pd " %s (%s)\n", frame_index, + function_name.ToCString(), url.ToCString()); } - frame_strings->Add(chars); - return strlen(chars); } const char* StackTrace::ToCStringInternal(intptr_t* frame_index, intptr_t max_frames) const { Zone* zone = Thread::Current()->zone(); - Function& function = Function::Handle(); - Code& code = Code::Handle(); + Function& function = Function::Handle(zone); + Code& code = Code::Handle(zone); + GrowableArray inlined_functions; + GrowableArray inlined_token_positions; + TextBuffer buffer(1024); + // Iterate through the stack frames and create C string description // for each frame. - intptr_t total_len = 0; - GrowableArray frame_strings; for (intptr_t i = 0; (i < Length()) && (*frame_index < max_frames); i++) { - function = FunctionAtFrame(i); - if (function.IsNull()) { + code = CodeAtFrame(i); + if (code.IsNull()) { // Check for a null function, which indicates a gap in a StackOverflow or // OutOfMemory trace. - if ((i < (Length() - 1)) && - (FunctionAtFrame(i + 1) != Function::null())) { - const char* kTruncated = "...\n...\n"; - intptr_t truncated_len = strlen(kTruncated) + 1; - char* chars = zone->Alloc(truncated_len); - OS::SNPrint(chars, truncated_len, "%s", kTruncated); - frame_strings.Add(chars); - total_len += truncated_len; + if ((i < (Length() - 1)) && (CodeAtFrame(i + 1) != Code::null())) { + buffer.AddString("...\n...\n"); ASSERT(PcOffsetAtFrame(i) != Smi::null()); // To account for gap frames. (*frame_index) += Smi::Value(PcOffsetAtFrame(i)); } } else { - code = CodeAtFrame(i); - ASSERT(function.raw() == code.function()); - uword pc = code.PayloadStart() + Smi::Value(PcOffsetAtFrame(i)); + // Stub code is not included in the stack trace. + ASSERT(code.IsFunctionCode()); + + intptr_t pc_offset = Smi::Value(PcOffsetAtFrame(i)); if (code.is_optimized() && expand_inlined() && !FLAG_precompiled_runtime) { - // Traverse inlined frames. - for (InlinedFunctionsIterator it(code, pc); - !it.Done() && (*frame_index < max_frames); it.Advance()) { - function = it.function(); - if (function.is_visible() || FLAG_show_invisible_frames) { - code = it.code(); - ASSERT(function.raw() == code.function()); - uword pc = it.pc(); - ASSERT(pc != 0); - ASSERT(code.PayloadStart() <= pc); - ASSERT(pc < (code.PayloadStart() + code.Size())); - total_len += PrintOneStackTrace(zone, &frame_strings, pc, function, - code, *frame_index); - (*frame_index)++; // To account for inlined frames. + code.GetInlinedFunctionsAtReturnAddress(pc_offset, &inlined_functions, + &inlined_token_positions); + ASSERT(inlined_functions.length() >= 1); + for (intptr_t j = inlined_functions.length() - 1; j >= 0; j--) { + if (inlined_functions[j]->is_visible() || + FLAG_show_invisible_frames) { + PrintStackTraceFrame(zone, &buffer, *inlined_functions[j], + inlined_token_positions[j], *frame_index); + (*frame_index)++; } } } else { + function = code.function(); if (function.is_visible() || FLAG_show_invisible_frames) { - total_len += PrintOneStackTrace(zone, &frame_strings, pc, function, - code, *frame_index); + uword pc = code.PayloadStart() + pc_offset; + const TokenPosition token_pos = code.GetTokenIndexOfPC(pc); + PrintStackTraceFrame(zone, &buffer, function, token_pos, + *frame_index); (*frame_index)++; } } } } - // Now concatenate the frame descriptions into a single C string. - char* chars = zone->Alloc(total_len + 1); - intptr_t index = 0; - for (intptr_t i = 0; i < frame_strings.length(); i++) { - index += OS::SNPrint((chars + index), (total_len + 1 - index), "%s", - frame_strings[i]); - } - chars[total_len] = '\0'; - return chars; + return buffer.Steal(); } diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 69dc8146685..89901f7948c 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -4784,10 +4784,20 @@ class Code : public Object { // function except the top-of-stack is the position of the call to the next // function. The stack will be empty if we lack the metadata to produce it, // which happens for stub code. - void GetInlinedFunctionsAt( + // The pc offset is interpreted as an instruction address (as needed by the + // disassembler or the top frame of a profiler sample). + void GetInlinedFunctionsAtInstruction( intptr_t pc_offset, GrowableArray* functions, GrowableArray* token_positions) const; + // Same as above, expect the pc is intepreted as a return address (as needed + // for a stack trace or the bottom frames of a profiler sample). + void GetInlinedFunctionsAtReturnAddress( + intptr_t pc_offset, + GrowableArray* functions, + GrowableArray* token_positions) const { + GetInlinedFunctionsAtInstruction(pc_offset - 1, functions, token_positions); + } NOT_IN_PRODUCT(void PrintJSONInlineIntervals(JSONObject* object) const); void DumpInlineIntervals() const; @@ -8388,8 +8398,6 @@ class StackTrace : public Instance { intptr_t Length() const; - RawFunction* FunctionAtFrame(intptr_t frame_index) const; - RawCode* CodeAtFrame(intptr_t frame_index) const; void SetCodeAtFrame(intptr_t frame_index, const Code& code) const; diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc index 10b2e9727b2..5290e0697d0 100644 --- a/runtime/vm/profiler_service.cc +++ b/runtime/vm/profiler_service.cc @@ -1103,8 +1103,9 @@ class ProfileCodeInlinedFunctionsCache : public ValueObject { CacheEntry* cache_entry = &cache_[NextFreeIndex()]; cache_entry->pc = pc; cache_entry->offset = offset; - code.GetInlinedFunctionsAt(offset, &(cache_entry->inlined_functions), - &(cache_entry->inlined_token_positions)); + code.GetInlinedFunctionsAtInstruction( + offset, &(cache_entry->inlined_functions), + &(cache_entry->inlined_token_positions)); if (cache_entry->inlined_functions.length() == 0) { *inlined_functions = NULL; *inlined_token_positions = NULL; diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc index 647f87f9a3d..bcb32618de5 100644 --- a/runtime/vm/profiler_test.cc +++ b/runtime/vm/profiler_test.cc @@ -2453,7 +2453,8 @@ static uword FindPCForTokenPosition(const Code& code, GrowableArray functions; GrowableArray token_positions; for (intptr_t pc_offset = 0; pc_offset < code.Size(); pc_offset++) { - code.GetInlinedFunctionsAt(pc_offset, &functions, &token_positions); + code.GetInlinedFunctionsAtInstruction(pc_offset, &functions, + &token_positions); if (token_positions[0] == tp) { return code.PayloadStart() + pc_offset; } diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 2f2cf6046e5..8b31c657b79 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -1144,11 +1144,11 @@ class RawCode : public RawObject { // (code-offset, function, code) triples. NOT_IN_PRECOMPILED(RawArray* static_calls_target_table_); NOT_IN_PRECOMPILED(RawArray* inlined_id_to_function_); + NOT_IN_PRECOMPILED(RawCodeSourceMap* code_source_map_); // If return_address_metadata_ is a Smi, it is the offset to the prologue. // Else, return_address_metadata_ is null. NOT_IN_PRECOMPILED(RawObject* return_address_metadata_); NOT_IN_PRECOMPILED(RawLocalVarDescriptors* var_descriptors_); - NOT_IN_PRECOMPILED(RawCodeSourceMap* code_source_map_); NOT_IN_PRECOMPILED(RawArray* comments_); RawObject** to() { #if defined(DART_PRECOMPILED_RUNTIME)