Skip to content

VM: Crash while handling reference from an instance constant to a class which is not loaded yet #37533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
alexmarkov opened this issue Jul 15, 2019 · 16 comments
Assignees
Labels
area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@alexmarkov
Copy link
Contributor

This appears on the Flutter tests which use pub build_runner.

Repro:

  1. In flutter engine checkout, edit DEPS to use the latest Dart SDK
  2. cd FLUTTER_ENGINE/src; gclient sync
  3. flutter/tools/gn --full-dart-sdk
  4. ninja -j 80 -C out/host_debug
  5. In flutter/flutter checkout cd FLUTTER/packages/flutter_tools
  6. rm -rf ../../bin/cache/dart-sdk
  7. ln -s FLUTTER_ENGINE/src/out/host_debug/dart-sdk ../../bin/cache/dart-sdk
  8. ../../bin/cache/dart-sdk/bin/pub run build_runner test -- -rcompact -j1 --no-color

This crashes with

./../third_party/dart/runtime/vm/compiler/frontend/bytecode_reader.cc: 1295: error: unreachable code

Stack trace:

#2  0x000000000135cc54 in dart::Assert::Fail (this=<optimized out>, format=<optimized out>) at ../../third_party/dart/runtime/platform/assert.cc:44
#3  0x000000000179de61 in dart::kernel::BytecodeReaderHelper::ReadObjectContents (this=<optimized out>, header=<optimized out>)
    at ../../third_party/dart/runtime/platform/assert.h:30
#4  0x00000000017a28be in dart::kernel::BytecodeReaderHelper::ReadClassDeclaration (this=0x7fffd55fdf90, cls=...)
    at ../../third_party/dart/runtime/vm/compiler/frontend/bytecode_reader.cc:2332
#5  0x00000000017a556a in dart::kernel::BytecodeReader::LoadClassDeclaration (cls=...)
    at ../../third_party/dart/runtime/vm/compiler/frontend/bytecode_reader.cc:3131
#6  0x00000000014c6867 in dart::ClassFinalizer::LoadClassMembers (cls=...) at ../../third_party/dart/runtime/vm/class_finalizer.cc:1199
#7  0x000000000158a9b1 in dart::Class::EnsureIsFinalized (this=0x7fffc827c0f0, thread=0x7fffc8004e50) at ../../third_party/dart/runtime/vm/object.cc:3579
#8  0x00000000017ab805 in dart::kernel::ConstantEvaluator::EvaluateConstant (this=0x7fffd55fe3a8, constant_offset=<optimized out>)
    at ../../third_party/dart/runtime/vm/compiler/frontend/constant_evaluator.cc:445
#9  0x00000000017aa814 in dart::kernel::ConstantEvaluator::EvaluateConstantExpression (this=0x7fffd55fe3a8, constant_offset=140736773216912)
    at ../../third_party/dart/runtime/vm/compiler/frontend/constant_evaluator.cc:325
#10 0x000000000155e945 in dart::kernel::KernelLoader::LoadNativeExtensionLibraries (this=0x7fffd55fe760)
    at ../../third_party/dart/runtime/vm/kernel_loader.cc:673
#11 0x000000000155c7e0 in dart::kernel::KernelLoader::LoadProgram (this=0x7fffd55fe760, process_pending_classes=<optimized out>)
    at ../../third_party/dart/runtime/vm/kernel_loader.cc:762
#12 0x000000000155c305 in dart::kernel::KernelLoader::LoadEntireProgram (program=<optimized out>, process_pending_classes=<optimized out>)
    at ../../third_party/dart/runtime/vm/kernel_loader.cc:301
#13 0x00000000018a835d in Dart_LoadScriptFromKernel (buffer=0x7fffce1cd010 "\220\253\315", <incomplete sequence \357>, buffer_size=31665888)
    at ../../third_party/dart/runtime/vm/dart_api_impl.cc:5161
#14 0x0000000001349a59 in dart::bin::IsolateSetupHelper (isolate=0x7fffc8000ee0, is_main_isolate=false, 
    script_uri=0x7fffec03d930 "file:///usr/local/google/home/alexmarkov/work/flutter/packages/flutter_tools/bar/test/integration.shard/lifetime_test.dart.vm_test.vm.app.dill", packages_config=<optimized out>, isolate_run_app_snapshot=false, flags=<optimized out>, error=0x7fffd55fed90, exit_code=0x7fffd55fed0c)
    at ../../third_party/dart/runtime/bin/main.cc:377
#15 0x00000000013483bb in dart::bin::CreateIsolateGroupAndSetupHelper (is_main_isolate=<optimized out>, script_uri=<optimized out>, name=<optimized out>, 
    package_root=<optimized out>, packages_config=<optimized out>, flags=<optimized out>, callback_data=<optimized out>, error=<optimized out>, 
    exit_code=<optimized out>) at ../../third_party/dart/runtime/bin/main.cc:738
#16 0x0000000001348eae in dart::bin::CreateIsolateGroupAndSetup (
    script_uri=0x7fffec03d930 "file:///usr/local/google/home/alexmarkov/work/flutter/packages/flutter_tools/bar/test/integration.shard/lifetime_test.dart.vm_test.vm.app.dill", main=0x7fffec03da70 "main", package_root=0x0, 
    package_config=0x7fffec29be00 "file:///usr/local/google/home/alexmarkov/work/flutter/packages/flutter_tools/bar/.packages", flags=0x7fffd55feda0, 
    callback_data=0x7fffd80087b0, error=<optimized out>) at ../../third_party/dart/runtime/bin/main.cc:779
#17 0x0000000001483a88 in dart::SpawnIsolateTask::Run (this=0x7fffec03db40) at ../../third_party/dart/runtime/lib/isolate.cc:158
#18 0x000000000169f99a in dart::ThreadPool::Worker::Loop (this=0x7fffec5ccee0) at ../../third_party/dart/runtime/vm/thread_pool.cc:380
#19 0x000000000169f830 in dart::ThreadPool::Worker::Main (args=140737158893280) at ../../third_party/dart/runtime/vm/thread_pool.cc:435
#20 0x0000000001609e53 in dart::ThreadStart (data_ptr=<optimized out>) at ../../third_party/dart/runtime/vm/os_thread_linux.cc:134

Pub build_runner uses concatenated dill files, so multiple kernel components are loaded one by one. The problem is that when reading instance constant, class Library:'package:meta/meta.dart' Class: _Experimental@49056369 is not loaded yet (it is probably coming from a kernel component which is loaded after the current component). An attempt to finalize such class triggers its lazy loading. Normally, lazy loading of classes could happen only if class is loaded from bytecode, so bytecode loading is triggered from

sdk/runtime/vm/object.cc

Lines 3551 to 3564 in d0cb753

void Class::EnsureDeclarationLoaded() const {
if (!is_declaration_loaded()) {
#if defined(DART_PRECOMPILED_RUNTIME)
UNREACHABLE();
#else
// Loading of class declaration can be postponed until needed
// if class comes from bytecode.
ASSERT(is_declared_in_bytecode());
kernel::BytecodeReader::LoadClassDeclaration(*this);
ASSERT(is_declaration_loaded());
ASSERT(is_type_finalized());
#endif
}
}

Bytecode is not actually used here. As flutter engine builds Dart VM without asserts they are not triggered until bytecode reading crashes.

With https://dart-review.googlesource.com/c/sdk/+/109103 applied a more informative error message is printed:

./../third_party/dart/runtime/vm/compiler/frontend/constant_evaluator.cc: 448: error: Trying to evaluate an instance constant which references class Library:'package:meta/meta.dart' Class: _Experimental@50056369, which is not loaded yet.

/cc @a-siva @aartbik

@alexmarkov alexmarkov added area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) labels Jul 15, 2019
@aartbik
Copy link
Contributor

aartbik commented Jul 15, 2019

Thanks Alex. Related to #36220

dart-bot pushed a commit that referenced this issue Jul 15, 2019
…oaded yet

Issue: #37533
Issue: #36391

Change-Id: Ib1addecabd60c7de4387afe5f6b8d7cf856a548b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/109103
Auto-Submit: Alexander Markov <[email protected]>
Reviewed-by: Aart Bik <[email protected]>
Commit-Queue: Alexander Markov <[email protected]>
@aartbik
Copy link
Contributor

aartbik commented Jul 15, 2019

Rolled to "6eb357c7fea8b49603941f2ea4e541405e3253bb" (Alex' informative message) and was able to reproduce. Thanks Alex!

Running tests...

00:00 +0: loading test/general.shard/asset_bundle_variant_test.dart
../../third_party/dart/runtime/vm/compiler/frontend/constant_evaluator.cc: 449: error: Trying to evaluate an instance constant which references class Library:'package:meta/meta.dart' Class: _Experimental@66056369, which is not loaded yet.
version=2.5.0-dev.1.0.flutter-6eb357c7fe (Mon Jul 15 19:22:16 2019 +0000) on "linux_x64"
thread=107267, isolate=main(0x7f97e0000e60)
  pc 0x000000000160ecfc fp 0x00007f97e747e0d0 /usr/local/google/home/ajcbik/drive2/flutter/engine/src/out/host_debug/dart-sdk/bin/dart+0x140ecfc
-- End of DumpStackTrace

@aartbik
Copy link
Contributor

aartbik commented Jul 15, 2019

I have a very simple fix for this which we can apply right away (independent of the discussion whether moving the evaluation points will be beneficial for other reasons).

Since the kernel_loader
(1) is only interested in whether a constant is an "external_name" or "pragma" class
(2) ensures that these classes are loaded when tested (viz. EnsureExternal/PragmaClassIsLookedUp)

when evaluating a constant for the kernel loader, we can simply stop at instance constants for which the class declaration can never be external or pragma. This incidentally also allows me to remove the somewhat ad-hoc cyclic evaluation detection.

@aartbik aartbik self-assigned this Jul 15, 2019
@mraleph
Copy link
Member

mraleph commented Jul 16, 2019

Drive-by question: are we sure concatenated dills produced by build runner are valid with respect to (reverse) topological order that we are expecting from them? I would expected that dependencies are loaded before the other things that depend on them?

dart-bot pushed a commit that referenced this issue Jul 18, 2019
Rationale:
Only look for "external" and "pragma" to avoid
cyclic or unloaded class evaluation

#37533
#36220


Change-Id: I78ef5dcd2c7e7dee0d196394320e994f8aa8a695
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/109300
Commit-Queue: Aart Bik <[email protected]>
Reviewed-by: Alexander Markov <[email protected]>
@aartbik aartbik closed this as completed Jul 18, 2019
@mraleph
Copy link
Member

mraleph commented Jul 19, 2019

My question from #37533 (comment) still stands - in general the expectation is that we should not have concatenated dill files that are out of reverse topological order with respect to the dependencies. This really simplifies things.

@grouma Gary do you happen to know whom we should talk to about build_runner and dill files it produces? This issue seems strikingly similar to what we already had internally - and I would like to understand if we can insure proper ordering of the components within a concatenated dill file.

Another question I have is why build_runner is concatenating dill files instead of using dill lists which is what is used in G3. I think @jensjoha mentioned to me that he sees gigabytes of space wasted on this when he runs Flutter tests.

@a-siva
Copy link
Contributor

a-siva commented Jul 19, 2019

Concatenated dill files is not the only case where this issue arises, we have to also account for it in the Fuchsia '.dilp' situation where individual dil files are created for packages and loaded.

@grouma
Copy link
Member

grouma commented Jul 19, 2019

@jakemac53 will be your best point of contact for build_runner details (he's out till Tuesday I believe).

I thought we included the topological logic externally but I couldn't find such logic in build_vm_compilers:
https://github.com/dart-lang/build/tree/1bf071d04b7c5f70d4c5a078a86a34a7d98e8d46/build_vm_compilers

The concatenation logic is in the vm_entrypoint_builder:
https://github.com/dart-lang/build/blob/master/build_vm_compilers/lib/src/vm_entrypoint_builder.dart

@jakemac53
Copy link
Contributor

Actually it looks like we have a bug here where we add the root kernel file first https://github.com/dart-lang/build/blob/master/build_vm_compilers/lib/src/vm_entrypoint_builder.dart#L65. The rest of the transitive deps are in reverse dependency order though.

I can do a quick fix and we can see if it resolves things? We also want to change to use the kernel manifest files soon instead of fully concatenated dills.

@jakemac53
Copy link
Contributor

@a-siva Ok so I tried changing the order but it turns out we did it in that order intentionally. The reason is that the vm will run the first kernel file that contains a main method that it finds. This is in conflict with the reverse dependency ordering that it requires for concatenation. You end up running the first transitive dep which has a main - and this unfortunately quite common (and happens in 100% of vm tests) because the test framework bootstraps the original app and calls its main.

Any ideas how we should work around this?

@mraleph
Copy link
Member

mraleph commented Jul 29, 2019

@jakemac53 I think I am getting confused by terminology again (and the fact that we flipped the order in the implementation). The ordering which would make VM work is the following:

mod_0.dill
...
mod_N.dill

such that if mod_i.dill depends on mod_j.dill then i < j.

The main would be taken from the first mod_i.dill that has main.

@jakemac53
Copy link
Contributor

OK, just to clarify, if you have 3 modules [a, b, c] where a depends on b and b depends on c, then you would expect to get them in that order (a, b, c)?

@mraleph
Copy link
Member

mraleph commented Jul 29, 2019

@jakemac53 correct. I think this is the order that we are getting internally. The loading code would then load them in reverse order. (so that c then b then a).

@jakemac53
Copy link
Contributor

@mraleph presumably this only applies to the manifest files though? We aren't using those yet. For a regular concatenated file it seems like we should be doing the reverse ordering ourselves?

The issue with multiple main functions also still seems like a problem.

@mraleph
Copy link
Member

mraleph commented Jul 29, 2019

@jakemac53 the ordering is the same for manifest files and concatenated files AFAIK. (manifest files do not really exist for internal loading purposes - outer layers simply perform concatenation in memory and give the concatenated dill file to the kernel loader).

main should not be a problem - if the very first dill file in the manifest contains main - then it is guaranteed to be used.

@jakemac53
Copy link
Contributor

jakemac53 commented Jul 29, 2019

OK - I can try changing the ordering internally and externally and see how it goes.

I am surprised though that we haven't had more problems related to this before now...?

jakemac53 added a commit to dart-lang/build that referenced this issue Jul 29, 2019
jakemac53 added a commit to dart-lang/build that referenced this issue Jul 29, 2019
@jakemac53
Copy link
Contributor

jakemac53 commented Jul 29, 2019

I have updated the external build_vm_compilers to do the requested ordering, and published as version 1.0.2.

It turns out that internal already had the correct ordering.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

6 participants