mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
Include metadata in AOT to expand inline frames in stack traces and provide line numbers.
Also fix stack trace collection to always include invisible frames. It can happen that a visible function is inlined into an invisible function, and we don't expand inlined frames until we print a stack trace. dart2js product x64: compile time: 12.459s VMIsolate(CodeSize): 152292 Isolate(CodeSize): 3343117 ReadOnlyData(CodeSize): 3728928 Instructions(CodeSize): 8677600 Total(CodeSize): 15901937 -> compile time: 14.195s (+13%) VMIsolate(CodeSize): 174034 Isolate(CodeSize): 3892418 (+16%) ReadOnlyData(CodeSize): 5036320 (+35%) Instructions(CodeSize): 8682624 Total(CodeSize): 17785396 (+12%) R=asiva@google.com Review-Url: https://codereview.chromium.org/2687143005 .
This commit is contained in:
parent
f0e657015d
commit
cfe5e5f075
20 changed files with 306 additions and 118 deletions
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
// Test correct source positions in stack trace with optimized functions.
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
// (1) Test normal exception.
|
||||
foo(x) => bar(x);
|
||||
|
||||
bar(x) {
|
||||
if (x == null) throw 42; // throw at position 11:18
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
test1() {
|
||||
// First unoptimized.
|
||||
try {
|
||||
foo(null);
|
||||
Expect.fail("Unreachable");
|
||||
} catch (e, stacktrace) {
|
||||
String s = stacktrace.toString();
|
||||
print(s);
|
||||
Expect.isFalse(s.contains("-1:-1"), "A");
|
||||
Expect.isTrue(s.contains("optimized_stacktrace_line_and_column_test.dart:11:18"), "B");
|
||||
}
|
||||
|
||||
// Optimized.
|
||||
for (var i=0; i<10000; i++) foo(42);
|
||||
try {
|
||||
foo(null);
|
||||
Expect.fail("Unreachable");
|
||||
} catch (e, stacktrace) {
|
||||
String s = stacktrace.toString();
|
||||
print(s);
|
||||
Expect.isFalse(s.contains("-1:-1"), "C");
|
||||
Expect.isTrue(s.contains("optimized_stacktrace_line_and_column_test.dart:11:18"), "D");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// (2) Test checked mode exceptions.
|
||||
maximus(x) => moritz(x);
|
||||
|
||||
moritz(x) {
|
||||
if (x == 333) return 42 ? 0 : 1; // Throws in checked mode.
|
||||
if (x == 777) {
|
||||
bool b = x; // Throws in checked mode.
|
||||
return b;
|
||||
}
|
||||
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
test2() {
|
||||
for (var i=0; i<100000; i++) maximus(42);
|
||||
try {
|
||||
maximus(333);
|
||||
} catch (e, stacktrace) {
|
||||
String s = stacktrace.toString();
|
||||
print(s);
|
||||
Expect.isTrue(s.contains("maximus"), "E");
|
||||
Expect.isTrue(s.contains("moritz"), "F");
|
||||
Expect.isFalse(s.contains("-1:-1"), "G");
|
||||
}
|
||||
|
||||
try {
|
||||
maximus(777);
|
||||
} catch (e, stacktrace) {
|
||||
String s = stacktrace.toString();
|
||||
print(s);
|
||||
Expect.isTrue(s.contains("maximus"), "H");
|
||||
Expect.isTrue(s.contains("moritz"), "I");
|
||||
Expect.isFalse(s.contains("-1:-1"), "J");
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
test1();
|
||||
test2();
|
||||
}
|
|
@ -19,8 +19,9 @@ test1() {
|
|||
Expect.fail("Unreachable");
|
||||
} catch (e, stacktrace) {
|
||||
String s = stacktrace.toString();
|
||||
Expect.isFalse(s.contains("-1:-1"));
|
||||
Expect.isTrue(s.contains("11:18"));
|
||||
print(s);
|
||||
Expect.isFalse(s.contains("-1:-1"), "A");
|
||||
Expect.isTrue(s.contains("optimized_stacktrace_line_test.dart:11"), "B");
|
||||
}
|
||||
|
||||
// Optimized.
|
||||
|
@ -30,8 +31,9 @@ test1() {
|
|||
Expect.fail("Unreachable");
|
||||
} catch (e, stacktrace) {
|
||||
String s = stacktrace.toString();
|
||||
Expect.isFalse(s.contains("-1:-1"));
|
||||
Expect.isTrue(s.contains("11:18"));
|
||||
print(s);
|
||||
Expect.isFalse(s.contains("-1:-1"), "C");
|
||||
Expect.isTrue(s.contains("optimized_stacktrace_line_test.dart:11"), "D");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,9 +58,9 @@ test2() {
|
|||
} catch (e, stacktrace) {
|
||||
String s = stacktrace.toString();
|
||||
print(s);
|
||||
Expect.isTrue(s.contains("maximus"));
|
||||
Expect.isTrue(s.contains("moritz"));
|
||||
Expect.isFalse(s.contains("-1:-1"));
|
||||
Expect.isTrue(s.contains("maximus"), "E");
|
||||
Expect.isTrue(s.contains("moritz"), "F");
|
||||
Expect.isFalse(s.contains("-1:-1"), "G");
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -66,9 +68,9 @@ test2() {
|
|||
} catch (e, stacktrace) {
|
||||
String s = stacktrace.toString();
|
||||
print(s);
|
||||
Expect.isTrue(s.contains("maximus"));
|
||||
Expect.isTrue(s.contains("moritz"));
|
||||
Expect.isFalse(s.contains("-1:-1"));
|
||||
Expect.isTrue(s.contains("maximus"), "H");
|
||||
Expect.isTrue(s.contains("moritz"), "I");
|
||||
Expect.isFalse(s.contains("-1:-1"), "J");
|
||||
}
|
||||
}
|
||||
|
|
@ -149,7 +149,8 @@ dart/simd128float32_test: Skip # compilers not aware of Simd128
|
|||
|
||||
[ $compiler == dart2js ]
|
||||
# The source positions do not match with dart2js.
|
||||
dart/optimized_stacktrace_test: RuntimeError
|
||||
dart/optimized_stacktrace_line_test: RuntimeError
|
||||
dart/optimized_stacktrace_line_and_column_test: RuntimeError
|
||||
|
||||
# Methods can be missing in dart2js stack traces due to inlining. Also when
|
||||
# minifying they can be renamed, which is issue 7953.
|
||||
|
@ -169,7 +170,8 @@ cc/Int8ListLengthMaxElements: Skip # Issue 23536, uses 1 GB memory.
|
|||
cc/FindCodeObject: SkipSlow # Takes more than 8 minutes. Issue 17440
|
||||
|
||||
[ $compiler == dart2analyzer ]
|
||||
dart/optimized_stacktrace_test: StaticWarning
|
||||
dart/optimized_stacktrace_line_test: StaticWarning
|
||||
dart/optimized_stacktrace_line_and_column_test: StaticWarning
|
||||
|
||||
[ $compiler == dart2analyzer && $builder_tag == strong ]
|
||||
*: Skip # Issue 28649
|
||||
|
@ -256,14 +258,12 @@ cc/CreateMirrorSystem: SkipByDesign # Imports dart:mirrors
|
|||
cc/CoreSnapshotSize: SkipByDesign # Imports dart:mirrors
|
||||
cc/StandaloneSnapshotSize: SkipByDesign # Imports dart:mirrors
|
||||
|
||||
[ $runtime == dart_precompiled ]
|
||||
# StackTraces in precompilation omit inlined frames.
|
||||
dart/inline_stack_frame_test: Pass, RuntimeError
|
||||
dart/optimized_stacktrace_test: Pass, RuntimeError
|
||||
dart/data_uri_spawn_test: SkipByDesign # Isolate.spawnUri
|
||||
[ $compiler == app_jit ]
|
||||
dart/optimized_stacktrace_line_and_column_test: RuntimeError,OK # app-jit lacks column information
|
||||
|
||||
[ $compiler == app_jit || $compiler == precompiler ]
|
||||
dart/optimized_stacktrace_test: SkipByDesign # Requires line numbers
|
||||
[ $runtime == dart_precompiled ]
|
||||
dart/optimized_stacktrace_line_and_column_test: RuntimeError,OK # AOT lacks column information
|
||||
dart/data_uri_spawn_test: SkipByDesign # Isolate.spawnUri
|
||||
|
||||
[ $runtime == vm && $mode == product ]
|
||||
cc/IsolateSetCheckedMode: Fail,OK # Expects exact type name.
|
||||
|
|
|
@ -1567,12 +1567,12 @@ class CodeSerializationCluster : public SerializationCluster {
|
|||
s->Push(code->ptr()->exception_handlers_);
|
||||
s->Push(code->ptr()->pc_descriptors_);
|
||||
s->Push(code->ptr()->stackmaps_);
|
||||
s->Push(code->ptr()->inlined_id_to_function_);
|
||||
s->Push(code->ptr()->code_source_map_);
|
||||
|
||||
if (s->kind() == Snapshot::kAppJIT) {
|
||||
s->Push(code->ptr()->deopt_info_array_);
|
||||
s->Push(code->ptr()->static_calls_target_table_);
|
||||
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_));
|
||||
}
|
||||
}
|
||||
|
@ -1621,12 +1621,12 @@ class CodeSerializationCluster : public SerializationCluster {
|
|||
s->WriteRef(code->ptr()->exception_handlers_);
|
||||
s->WriteRef(code->ptr()->pc_descriptors_);
|
||||
s->WriteRef(code->ptr()->stackmaps_);
|
||||
s->WriteRef(code->ptr()->inlined_id_to_function_);
|
||||
s->WriteRef(code->ptr()->code_source_map_);
|
||||
|
||||
if (s->kind() == Snapshot::kAppJIT) {
|
||||
s->WriteRef(code->ptr()->deopt_info_array_);
|
||||
s->WriteRef(code->ptr()->static_calls_target_table_);
|
||||
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_));
|
||||
}
|
||||
|
||||
|
@ -1691,6 +1691,10 @@ class CodeDeserializationCluster : public DeserializationCluster {
|
|||
code->ptr()->pc_descriptors_ =
|
||||
reinterpret_cast<RawPcDescriptors*>(d->ReadRef());
|
||||
code->ptr()->stackmaps_ = reinterpret_cast<RawArray*>(d->ReadRef());
|
||||
code->ptr()->inlined_id_to_function_ =
|
||||
reinterpret_cast<RawArray*>(d->ReadRef());
|
||||
code->ptr()->code_source_map_ =
|
||||
reinterpret_cast<RawCodeSourceMap*>(d->ReadRef());
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (d->kind() == Snapshot::kAppJIT) {
|
||||
|
@ -1698,10 +1702,6 @@ class CodeDeserializationCluster : public DeserializationCluster {
|
|||
reinterpret_cast<RawArray*>(d->ReadRef());
|
||||
code->ptr()->static_calls_target_table_ =
|
||||
reinterpret_cast<RawArray*>(d->ReadRef());
|
||||
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
|
||||
|
@ -1710,8 +1710,6 @@ class CodeDeserializationCluster : public DeserializationCluster {
|
|||
} 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -315,6 +315,24 @@ RawCodeSourceMap* CodeSourceMapBuilder::Finalize() {
|
|||
}
|
||||
|
||||
|
||||
void CodeSourceMapBuilder::WriteChangePosition(TokenPosition pos) {
|
||||
stream_.Write<uint8_t>(kChangePosition);
|
||||
if (FLAG_precompiled_mode) {
|
||||
intptr_t line = -1;
|
||||
intptr_t inline_id = buffered_inline_id_stack_.Last();
|
||||
if (inline_id < inline_id_to_function_.length()) {
|
||||
const Function* function = inline_id_to_function_[inline_id];
|
||||
Script& script = Script::Handle(function->script());
|
||||
line = script.GetTokenLineUsingLineStarts(pos);
|
||||
}
|
||||
stream_.Write<int32_t>(static_cast<int32_t>(line));
|
||||
} else {
|
||||
stream_.Write<int32_t>(static_cast<int32_t>(pos.value()));
|
||||
}
|
||||
written_token_pos_stack_.Last() = pos;
|
||||
}
|
||||
|
||||
|
||||
void CodeSourceMapReader::GetInlinedFunctionsAt(
|
||||
int32_t pc_offset,
|
||||
GrowableArray<const Function*>* function_stack,
|
||||
|
|
|
@ -182,11 +182,7 @@ class CodeSourceMapBuilder : public ZoneAllocated {
|
|||
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 WriteChangePosition(TokenPosition pos);
|
||||
void BufferAdvancePC(int32_t distance) { buffered_pc_offset_ += distance; }
|
||||
void WriteAdvancePC(int32_t distance) {
|
||||
stream_.Write<uint8_t>(kAdvancePC);
|
||||
|
|
|
@ -1040,12 +1040,6 @@ void FlowGraphCompiler::FinalizeStaticCallTargetsTable(const Code& code) {
|
|||
|
||||
|
||||
void FlowGraphCompiler::FinalizeCodeSourceMap(const Code& code) {
|
||||
if (FLAG_precompiled_mode) {
|
||||
// TODO(rmacnak): Include a filtered verion of this to produce stack traces
|
||||
// with inlined frames.
|
||||
return;
|
||||
}
|
||||
|
||||
const Array& inlined_id_array =
|
||||
Array::Handle(zone(), code_source_map_builder_->InliningIdToFunction());
|
||||
INC_STAT(Thread::Current(), total_code_size,
|
||||
|
|
|
@ -9077,6 +9077,54 @@ void Script::SetLocationOffset(intptr_t line_offset,
|
|||
}
|
||||
|
||||
|
||||
// Specialized for AOT compilation, which does this lookup for every token
|
||||
// position that could be part of a stack trace.
|
||||
intptr_t Script::GetTokenLineUsingLineStarts(
|
||||
TokenPosition target_token_pos) const {
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
Array& line_starts_array = Array::Handle(zone, line_starts());
|
||||
Smi& token_pos = Smi::Handle(zone);
|
||||
if (line_starts_array.IsNull()) {
|
||||
ASSERT(kind() != RawScript::kKernelTag);
|
||||
GrowableObjectArray& line_starts_list =
|
||||
GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
|
||||
const TokenStream& tkns = TokenStream::Handle(zone, tokens());
|
||||
TokenStream::Iterator tkit(zone, tkns, TokenPosition::kMinSource,
|
||||
TokenStream::Iterator::kAllTokens);
|
||||
intptr_t cur_line = line_offset() + 1;
|
||||
token_pos = Smi::New(0);
|
||||
line_starts_list.Add(token_pos);
|
||||
while (tkit.CurrentTokenKind() != Token::kEOS) {
|
||||
if (tkit.CurrentTokenKind() == Token::kNEWLINE) {
|
||||
cur_line++;
|
||||
token_pos = Smi::New(tkit.CurrentPosition().value() + 1);
|
||||
line_starts_list.Add(token_pos);
|
||||
}
|
||||
tkit.Advance();
|
||||
}
|
||||
line_starts_array = Array::MakeArray(line_starts_list);
|
||||
set_line_starts(line_starts_array);
|
||||
}
|
||||
|
||||
ASSERT(line_starts_array.Length() > 0);
|
||||
intptr_t offset = target_token_pos.value();
|
||||
intptr_t min = 0;
|
||||
intptr_t max = line_starts_array.Length() - 1;
|
||||
|
||||
// Binary search to find the line containing this offset.
|
||||
while (min < max) {
|
||||
int midpoint = (max - min + 1) / 2 + min;
|
||||
token_pos ^= line_starts_array.At(midpoint);
|
||||
if (token_pos.Value() > offset) {
|
||||
max = midpoint - 1;
|
||||
} else {
|
||||
min = midpoint;
|
||||
}
|
||||
}
|
||||
return min + 1; // Line numbers start at 1.
|
||||
}
|
||||
|
||||
|
||||
void Script::GetTokenLocation(TokenPosition token_pos,
|
||||
intptr_t* line,
|
||||
intptr_t* column,
|
||||
|
@ -9085,7 +9133,7 @@ void Script::GetTokenLocation(TokenPosition token_pos,
|
|||
Zone* zone = Thread::Current()->zone();
|
||||
|
||||
if (kind() == RawScript::kKernelTag) {
|
||||
const Array& line_starts_array = Array::Handle(line_starts());
|
||||
const Array& line_starts_array = Array::Handle(zone, line_starts());
|
||||
if (line_starts_array.IsNull()) {
|
||||
// Scripts in the AOT snapshot do not have a line starts array.
|
||||
*line = -1;
|
||||
|
@ -9099,13 +9147,13 @@ void Script::GetTokenLocation(TokenPosition token_pos,
|
|||
}
|
||||
ASSERT(line_starts_array.Length() > 0);
|
||||
intptr_t offset = token_pos.value();
|
||||
int min = 0;
|
||||
int max = line_starts_array.Length() - 1;
|
||||
intptr_t min = 0;
|
||||
intptr_t max = line_starts_array.Length() - 1;
|
||||
|
||||
// Binary search to find the line containing this offset.
|
||||
Smi& smi = Smi::Handle();
|
||||
Smi& smi = Smi::Handle(zone);
|
||||
while (min < max) {
|
||||
int midpoint = (max - min + 1) / 2 + min;
|
||||
intptr_t midpoint = (max - min + 1) / 2 + min;
|
||||
|
||||
smi ^= line_starts_array.At(midpoint);
|
||||
if (smi.Value() > offset) {
|
||||
|
@ -9114,7 +9162,7 @@ void Script::GetTokenLocation(TokenPosition token_pos,
|
|||
min = midpoint;
|
||||
}
|
||||
}
|
||||
*line = min + 1;
|
||||
*line = min + 1; // Line numbers start at 1.
|
||||
smi ^= line_starts_array.At(min);
|
||||
if (column != NULL) {
|
||||
*column = offset - smi.Value() + 1;
|
||||
|
@ -14087,21 +14135,13 @@ intptr_t Code::GetPrologueOffset() const {
|
|||
|
||||
|
||||
RawArray* Code::inlined_id_to_function() const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
return Array::null();
|
||||
#else
|
||||
return raw_ptr()->inlined_id_to_function_;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Code::set_inlined_id_to_function(const Array& value) const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
ASSERT(value.IsOld());
|
||||
StorePointer(&raw_ptr()->inlined_id_to_function_, value.raw());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -14466,8 +14506,8 @@ void Code::GetInlinedFunctionsAtInstruction(
|
|||
GrowableArray<TokenPosition>* token_positions) const {
|
||||
const CodeSourceMap& map = CodeSourceMap::Handle(code_source_map());
|
||||
if (map.IsNull()) {
|
||||
// Stub code.
|
||||
return;
|
||||
ASSERT(!IsFunctionCode());
|
||||
return; // VM stub or allocation stub.
|
||||
}
|
||||
const Array& id_map = Array::Handle(inlined_id_to_function());
|
||||
const Function& root = Function::Handle(function());
|
||||
|
@ -22367,11 +22407,15 @@ static void PrintStackTraceFrame(Zone* zone,
|
|||
zone, script.IsNull() ? String::New("Kernel") : script.url());
|
||||
intptr_t line = -1;
|
||||
intptr_t column = -1;
|
||||
if (!script.IsNull() && token_pos.IsReal()) {
|
||||
if (script.HasSource() || script.kind() == RawScript::kKernelTag) {
|
||||
script.GetTokenLocation(token_pos, &line, &column);
|
||||
} else {
|
||||
script.GetTokenLocation(token_pos, &line, NULL);
|
||||
if (FLAG_precompiled_mode) {
|
||||
line = token_pos.value();
|
||||
} else {
|
||||
if (!script.IsNull() && token_pos.IsReal()) {
|
||||
if (script.HasSource() || script.kind() == RawScript::kKernelTag) {
|
||||
script.GetTokenLocation(token_pos, &line, &column);
|
||||
} else {
|
||||
script.GetTokenLocation(token_pos, &line, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (column >= 0) {
|
||||
|
@ -22420,8 +22464,7 @@ const char* StackTrace::ToCStringInternal(const StackTrace& stack_trace_in,
|
|||
} else {
|
||||
ASSERT(code.IsFunctionCode());
|
||||
intptr_t pc_offset = Smi::Value(stack_trace.PcOffsetAtFrame(i));
|
||||
if (code.is_optimized() && stack_trace.expand_inlined() &&
|
||||
!FLAG_precompiled_mode) {
|
||||
if (code.is_optimized() && stack_trace.expand_inlined()) {
|
||||
code.GetInlinedFunctionsAtReturnAddress(pc_offset, &inlined_functions,
|
||||
&inlined_token_positions);
|
||||
ASSERT(inlined_functions.length() >= 1);
|
||||
|
|
|
@ -3576,6 +3576,7 @@ class Script : public Object {
|
|||
|
||||
void SetLocationOffset(intptr_t line_offset, intptr_t col_offset) const;
|
||||
|
||||
intptr_t GetTokenLineUsingLineStarts(TokenPosition token_pos) const;
|
||||
void GetTokenLocation(TokenPosition token_pos,
|
||||
intptr_t* line,
|
||||
intptr_t* column,
|
||||
|
@ -4408,6 +4409,14 @@ class CodeSourceMap : public Object {
|
|||
return UnsafeMutableNonPointer(&raw_ptr()->data()[0]);
|
||||
}
|
||||
|
||||
bool Equals(const CodeSourceMap& other) const {
|
||||
if (Length() != other.Length()) {
|
||||
return false;
|
||||
}
|
||||
NoSafepointScope no_safepoint;
|
||||
return memcmp(raw_ptr(), other.raw_ptr(), InstanceSize(Length())) == 0;
|
||||
}
|
||||
|
||||
void PrintToJSONObject(JSONObject* jsobj, bool ref) const;
|
||||
|
||||
private:
|
||||
|
@ -4659,20 +4668,12 @@ class Code : public Object {
|
|||
}
|
||||
|
||||
RawCodeSourceMap* code_source_map() const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
return CodeSourceMap::null();
|
||||
#else
|
||||
return raw_ptr()->code_source_map_;
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_code_source_map(const CodeSourceMap& code_source_map) const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
ASSERT(code_source_map.IsOld());
|
||||
StorePointer(&raw_ptr()->code_source_map_, code_source_map.raw());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Used during reloading (see object_reload.cc). Calls Reset on all ICDatas
|
||||
|
|
|
@ -495,6 +495,7 @@ void Precompiler::DoCompileAll(
|
|||
|
||||
ShareMegamorphicBuckets();
|
||||
DedupStackMaps();
|
||||
DedupCodeSourceMaps();
|
||||
DedupLists();
|
||||
|
||||
if (FLAG_dedup_instructions) {
|
||||
|
@ -984,6 +985,13 @@ void Precompiler::AddCalleesOf(const Function& function) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Array& inlined_functions =
|
||||
Array::Handle(Z, code.inlined_id_to_function());
|
||||
for (intptr_t i = 0; i < inlined_functions.Length(); i++) {
|
||||
target ^= inlined_functions.At(i);
|
||||
AddTypesOf(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -2341,6 +2349,50 @@ void Precompiler::DedupStackMaps() {
|
|||
}
|
||||
|
||||
|
||||
void Precompiler::DedupCodeSourceMaps() {
|
||||
class DedupCodeSourceMapsVisitor : public FunctionVisitor {
|
||||
public:
|
||||
explicit DedupCodeSourceMapsVisitor(Zone* zone)
|
||||
: zone_(zone),
|
||||
canonical_code_source_maps_(),
|
||||
code_(Code::Handle(zone)),
|
||||
code_source_map_(CodeSourceMap::Handle(zone)) {}
|
||||
|
||||
void Visit(const Function& function) {
|
||||
if (!function.HasCode()) {
|
||||
return;
|
||||
}
|
||||
code_ = function.CurrentCode();
|
||||
code_source_map_ = code_.code_source_map();
|
||||
ASSERT(!code_source_map_.IsNull());
|
||||
code_source_map_ = DedupCodeSourceMap(code_source_map_);
|
||||
code_.set_code_source_map(code_source_map_);
|
||||
}
|
||||
|
||||
RawCodeSourceMap* DedupCodeSourceMap(const CodeSourceMap& code_source_map) {
|
||||
const CodeSourceMap* canonical_code_source_map =
|
||||
canonical_code_source_maps_.LookupValue(&code_source_map);
|
||||
if (canonical_code_source_map == NULL) {
|
||||
canonical_code_source_maps_.Insert(
|
||||
&CodeSourceMap::ZoneHandle(zone_, code_source_map.raw()));
|
||||
return code_source_map.raw();
|
||||
} else {
|
||||
return canonical_code_source_map->raw();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Zone* zone_;
|
||||
CodeSourceMapSet canonical_code_source_maps_;
|
||||
Code& code_;
|
||||
CodeSourceMap& code_source_map_;
|
||||
};
|
||||
|
||||
DedupCodeSourceMapsVisitor visitor(Z);
|
||||
ProgramVisitor::VisitFunctions(&visitor);
|
||||
}
|
||||
|
||||
|
||||
void Precompiler::DedupLists() {
|
||||
class DedupListsVisitor : public FunctionVisitor {
|
||||
public:
|
||||
|
@ -2358,6 +2410,11 @@ void Precompiler::DedupLists() {
|
|||
list_ = DedupList(list_);
|
||||
code_.set_stackmaps(list_);
|
||||
}
|
||||
list_ = code_.inlined_id_to_function();
|
||||
if (!list_.IsNull()) {
|
||||
list_ = DedupList(list_);
|
||||
code_.set_inlined_id_to_function(list_);
|
||||
}
|
||||
}
|
||||
|
||||
list_ = function.parameter_types();
|
||||
|
|
|
@ -87,6 +87,27 @@ class StackMapKeyValueTrait {
|
|||
typedef DirectChainedHashMap<StackMapKeyValueTrait> StackMapSet;
|
||||
|
||||
|
||||
class CodeSourceMapKeyValueTrait {
|
||||
public:
|
||||
// Typedefs needed for the DirectChainedHashMap template.
|
||||
typedef const CodeSourceMap* Key;
|
||||
typedef const CodeSourceMap* Value;
|
||||
typedef const CodeSourceMap* Pair;
|
||||
|
||||
static Key KeyOf(Pair kv) { return kv; }
|
||||
|
||||
static Value ValueOf(Pair kv) { return kv; }
|
||||
|
||||
static inline intptr_t Hashcode(Key key) { return key->Length(); }
|
||||
|
||||
static inline bool IsKeyEqual(Pair pair, Key key) {
|
||||
return pair->Equals(*key);
|
||||
}
|
||||
};
|
||||
|
||||
typedef DirectChainedHashMap<CodeSourceMapKeyValueTrait> CodeSourceMapSet;
|
||||
|
||||
|
||||
class ArrayKeyValueTrait {
|
||||
public:
|
||||
// Typedefs needed for the DirectChainedHashMap template.
|
||||
|
@ -461,6 +482,7 @@ class Precompiler : public ValueObject {
|
|||
void SwitchICCalls();
|
||||
void ShareMegamorphicBuckets();
|
||||
void DedupStackMaps();
|
||||
void DedupCodeSourceMaps();
|
||||
void DedupLists();
|
||||
void DedupInstructions();
|
||||
void ResetPrecompilerState();
|
||||
|
|
|
@ -1139,12 +1139,12 @@ class RawCode : public RawObject {
|
|||
RawExceptionHandlers* exception_handlers_;
|
||||
RawPcDescriptors* pc_descriptors_;
|
||||
RawArray* stackmaps_;
|
||||
RawArray* inlined_id_to_function_;
|
||||
RawCodeSourceMap* code_source_map_;
|
||||
NOT_IN_PRECOMPILED(RawInstructions* active_instructions_);
|
||||
NOT_IN_PRECOMPILED(RawArray* deopt_info_array_);
|
||||
// (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_);
|
||||
|
|
|
@ -10,8 +10,7 @@ namespace dart {
|
|||
// Count the number of frames that are on the stack.
|
||||
intptr_t StackTraceUtils::CountFrames(Thread* thread,
|
||||
int skip_frames,
|
||||
const Function& async_function,
|
||||
bool count_invisible_frames) {
|
||||
const Function& async_function) {
|
||||
Zone* zone = thread->zone();
|
||||
intptr_t frame_count = 0;
|
||||
StackFrameIterator frames(StackFrameIterator::kDontValidateFrames);
|
||||
|
@ -27,9 +26,7 @@ intptr_t StackTraceUtils::CountFrames(Thread* thread,
|
|||
} else {
|
||||
code = frame->LookupDartCode();
|
||||
function = code.function();
|
||||
if (function.is_visible() || count_invisible_frames) {
|
||||
frame_count++;
|
||||
}
|
||||
frame_count++;
|
||||
if (!async_function_is_null &&
|
||||
(async_function.raw() == function.parent_function())) {
|
||||
return frame_count;
|
||||
|
@ -49,8 +46,7 @@ intptr_t StackTraceUtils::CollectFrames(Thread* thread,
|
|||
const Array& pc_offset_array,
|
||||
intptr_t array_offset,
|
||||
intptr_t count,
|
||||
int skip_frames,
|
||||
bool collect_invisible_frames) {
|
||||
int skip_frames) {
|
||||
Zone* zone = thread->zone();
|
||||
StackFrameIterator frames(StackFrameIterator::kDontValidateFrames);
|
||||
StackFrame* frame = frames.NextFrame();
|
||||
|
@ -66,13 +62,11 @@ intptr_t StackTraceUtils::CollectFrames(Thread* thread,
|
|||
} else {
|
||||
code = frame->LookupDartCode();
|
||||
function = code.function();
|
||||
if (function.is_visible() || collect_invisible_frames) {
|
||||
offset = Smi::New(frame->pc() - code.PayloadStart());
|
||||
code_array.SetAt(array_offset, code);
|
||||
pc_offset_array.SetAt(array_offset, offset);
|
||||
array_offset++;
|
||||
collected_frames_count++;
|
||||
}
|
||||
offset = Smi::New(frame->pc() - code.PayloadStart());
|
||||
code_array.SetAt(array_offset, code);
|
||||
pc_offset_array.SetAt(array_offset, offset);
|
||||
array_offset++;
|
||||
collected_frames_count++;
|
||||
}
|
||||
}
|
||||
frame = frames.NextFrame();
|
||||
|
|
|
@ -19,24 +19,20 @@ class StackTraceUtils : public AllStatic {
|
|||
/// Skips over the first |skip_frames|.
|
||||
/// If |async_function| is not null, stops at the function that has
|
||||
/// |async_function| as its parent.
|
||||
static intptr_t CountFrames(
|
||||
Thread* thread,
|
||||
int skip_frames,
|
||||
const Function& async_function,
|
||||
bool count_invisible_frames = FLAG_show_invisible_frames);
|
||||
static intptr_t CountFrames(Thread* thread,
|
||||
int skip_frames,
|
||||
const Function& async_function);
|
||||
|
||||
/// Collects |count| frames into |code_array| and |pc_offset_array|.
|
||||
/// Writing begins at |array_offset|.
|
||||
/// Skips over the first |skip_frames|.
|
||||
/// Returns the number of frames collected.
|
||||
static intptr_t CollectFrames(
|
||||
Thread* thread,
|
||||
const Array& code_array,
|
||||
const Array& pc_offset_array,
|
||||
intptr_t array_offset,
|
||||
intptr_t count,
|
||||
int skip_frames,
|
||||
bool collect_invisible_frames = FLAG_show_invisible_frames);
|
||||
static intptr_t CollectFrames(Thread* thread,
|
||||
const Array& code_array,
|
||||
const Array& pc_offset_array,
|
||||
intptr_t array_offset,
|
||||
intptr_t count,
|
||||
int skip_frames);
|
||||
|
||||
/// If |thread| has no async_stack_trace, does nothing.
|
||||
/// Populates |async_function| with the top function of the async stack
|
||||
|
|
|
@ -196,10 +196,6 @@ collection_removes_test: Crash # Issue 25911
|
|||
apply3_test: SkipByDesign # Imports dart:mirrors
|
||||
|
||||
[ $compiler == precompiler ]
|
||||
# Stacktraces in precompilation omit inlined frames.
|
||||
stacktrace_current_test: Pass, RuntimeError
|
||||
error_stack_trace1_test: Pass, RuntimeError
|
||||
|
||||
regexp/stack-overflow_test: RuntimeError, OK # Smaller limit with irregex interpreter
|
||||
big_integer_huge_mul_vm_test: Pass, Timeout # --no_intrinsify
|
||||
big_integer_parsed_mul_div_vm_test: Pass, Timeout # --no_intrinsify
|
||||
|
|
|
@ -15,6 +15,7 @@ void func3() {
|
|||
func2();
|
||||
} on Object catch(e, s) {
|
||||
var fullTrace = s.toString();
|
||||
print(fullTrace);
|
||||
Expect.isTrue(fullTrace.contains("func1"));
|
||||
Expect.isTrue(fullTrace.contains("func2"));
|
||||
Expect.isTrue(fullTrace.contains("func3"));
|
||||
|
|
|
@ -15,6 +15,7 @@ void func3() {
|
|||
func2();
|
||||
} on Object catch(e, s) {
|
||||
var fullTrace = s.toString();
|
||||
print(fullTrace);
|
||||
Expect.isTrue(fullTrace.contains("func1"));
|
||||
Expect.isTrue(fullTrace.contains("func2"));
|
||||
Expect.isTrue(fullTrace.contains("func3"));
|
||||
|
@ -36,6 +37,7 @@ int func5() {
|
|||
func4();
|
||||
} on Object catch(e, s) {
|
||||
var fullTrace = s.toString();
|
||||
print(fullTrace);
|
||||
Expect.isTrue(fullTrace.contains("func1"));
|
||||
Expect.isTrue(fullTrace.contains("func2"));
|
||||
Expect.isTrue(fullTrace.contains("func3"));
|
||||
|
|
|
@ -15,6 +15,7 @@ void func3() {
|
|||
func2();
|
||||
} on Object catch(e, s) {
|
||||
var fullTrace = s.toString();
|
||||
print(fullTrace);
|
||||
Expect.isTrue(fullTrace.contains("func1"));
|
||||
Expect.isTrue(fullTrace.contains("func2"));
|
||||
Expect.isTrue(fullTrace.contains("func3"));
|
||||
|
@ -35,6 +36,7 @@ int func5() {
|
|||
func4();
|
||||
} on Object catch(e, s) {
|
||||
var fullTrace = s.toString();
|
||||
print(fullTrace);
|
||||
Expect.isFalse(fullTrace.contains("func1"));
|
||||
Expect.isFalse(fullTrace.contains("func2"));
|
||||
Expect.isTrue(fullTrace.contains("func3"));
|
||||
|
|
|
@ -153,18 +153,6 @@ vm/regress_24517_test: Pass, Fail # Issue 24517.
|
|||
[ $compiler == precompiler && $runtime == dart_precompiled ]
|
||||
vm/regress_27671_test: Skip # Unsupported
|
||||
|
||||
[ $runtime == dart_precompiled ]
|
||||
# Stacktraces in precompilation omit inlined frames.
|
||||
full_stacktrace1_test: Pass, RuntimeError
|
||||
full_stacktrace2_test: Pass, RuntimeError
|
||||
full_stacktrace3_test: Pass, RuntimeError
|
||||
stack_trace_test: Pass, RuntimeError
|
||||
stacktrace_rethrow_error_test: Pass, RuntimeError
|
||||
stacktrace_rethrow_nonerror_test: Pass, RuntimeError
|
||||
stacktrace_test: Pass, RuntimeError
|
||||
vm/regress_28325_test: RuntimeError # Missing source position in AOT.
|
||||
|
||||
|
||||
[ $runtime == dart_precompiled || $mode == product ]
|
||||
# Imports dart:mirrors
|
||||
const_evaluation_test: SkipByDesign
|
||||
|
|
|
@ -249,8 +249,6 @@ no_allow_absolute_addresses_test: SkipByDesign # Not supported.
|
|||
link_natives_lazily_test: SkipByDesign # Not supported.
|
||||
|
||||
[ $compiler == precompiler ]
|
||||
# Stacktraces in precompilation omit inlined frames.
|
||||
assert_test: Pass, RuntimeError
|
||||
map_insert_remove_oom_test: Skip # Heap limit too low. Increasing iteration count to make a higher limit a meaningful test makes it too slow for simarm[64] bots.
|
||||
io/web_socket_test: Pass, RuntimeError # Issue 24674
|
||||
|
||||
|
|
Loading…
Reference in a new issue