Reapply "Use CodeSourceMap for stack traces (still JIT only)."

Include CodeSourceMaps in app-jit snapshots.

R=fschneider@google.com

Review-Url: https://codereview.chromium.org/2686623006 .
This commit is contained in:
Ryan Macnak
2017-02-08 16:43:47 -08:00
parent 504e6bb1dc
commit bee82fef8b
22 changed files with 322 additions and 197 deletions
@@ -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");
}
+13 -7
View File
@@ -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<RawArray*>(d->ReadRef());
code->ptr()->static_calls_target_table_ =
reinterpret_cast<RawArray*>(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<RawArray*>(d->ReadRef());
code->ptr()->code_source_map_ =
reinterpret_cast<RawCodeSourceMap*>(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:
+106 -31
View File
@@ -128,26 +128,86 @@ const TokenPosition CodeSourceMapBuilder::kInitialPosition =
CodeSourceMapBuilder::CodeSourceMapBuilder(
bool stack_traces_only,
const GrowableArray<intptr_t>& caller_inline_id,
const GrowableArray<TokenPosition>& inline_id_to_token_pos,
const GrowableArray<const Function*>& 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;
+56 -22
View File
@@ -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<intptr_t>& caller_inline_id,
const GrowableArray<TokenPosition>& inline_id_to_token_pos,
const GrowableArray<const Function*>& 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<uint8_t>(kChangePosition);
stream_.Write<int32_t>(static_cast<int32_t>(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<uint8_t>(kAdvancePC);
stream_.Write<int32_t>(advance_pc_peephole_);
advance_pc_peephole_ = 0;
}
void BufferAdvancePC(int32_t distance) { buffered_pc_offset_ += distance; }
void WriteAdvancePC(int32_t distance) {
stream_.Write<uint8_t>(kAdvancePC);
stream_.Write<int32_t>(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<uint8_t>(kPushFunction);
stream_.Write<int32_t>(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<uint8_t>(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<intptr_t> inline_id_stack_;
GrowableArray<TokenPosition> token_pos_stack_;
intptr_t buffered_pc_offset_;
GrowableArray<intptr_t> buffered_inline_id_stack_;
GrowableArray<TokenPosition> buffered_token_pos_stack_;
intptr_t written_pc_offset_;
GrowableArray<intptr_t> written_inline_id_stack_;
GrowableArray<TokenPosition> written_token_pos_stack_;
const GrowableArray<intptr_t>& caller_inline_id_;
const GrowableArray<TokenPosition>& 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);
};
+25 -19
View File
@@ -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);
}
}
}
+2 -1
View File
@@ -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++) {
+1 -1
View File
@@ -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_++;
+20 -11
View File
@@ -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<const Function*> fs;
GrowableArray<TokenPosition> tokens;
code.GetInlinedFunctionsAt(code.Size() - 1, &fs, &tokens);
code.GetInlinedFunctionsAtInstruction(code.Size() - 1, &fs, &tokens);
#endif
}
+5 -1
View File
@@ -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);
+2 -3
View File
@@ -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);
+2 -3
View File
@@ -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);
+2 -3
View File
@@ -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);
+2 -3
View File
@@ -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);
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
+41 -66
View File
@@ -14460,7 +14460,7 @@ RawStackMap* Code::GetStackMap(uint32_t pc_offset,
}
void Code::GetInlinedFunctionsAt(
void Code::GetInlinedFunctionsAtInstruction(
intptr_t pc_offset,
GrowableArray<const Function*>* functions,
GrowableArray<TokenPosition>* 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<RawCode*>(code_array.At(frame_index));
@@ -22337,13 +22331,11 @@ const char* StackTrace::ToCString() const {
}
static intptr_t PrintOneStackTrace(Zone* zone,
GrowableArray<char*>* 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<const Function*> inlined_functions;
GrowableArray<TokenPosition> 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<char*> 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<char>(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<char>(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();
}
+11 -3
View File
@@ -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<const Function*>* functions,
GrowableArray<TokenPosition>* 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<const Function*>* functions,
GrowableArray<TokenPosition>* 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;
+3 -2
View File
@@ -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;
+2 -1
View File
@@ -2453,7 +2453,8 @@ static uword FindPCForTokenPosition(const Code& code,
GrowableArray<const Function*> functions;
GrowableArray<TokenPosition> 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;
}
+1 -1
View File
@@ -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)