| // Copyright (c) 2020, 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. |
| |
| import "dart:io"; |
| import "package:expect/expect.dart"; |
| |
| // The sizes of these classes are co-prime multiples of the allocation unit to |
| // increase the likelihood that scavenging fails from fragmentation. |
| |
| // header + 13 fields = 7 allocation units |
| class A { |
| dynamic field1; |
| dynamic field2; |
| dynamic field3; |
| dynamic field4; |
| dynamic field5; |
| dynamic field6; |
| dynamic field7; |
| dynamic field8; |
| dynamic field9; |
| dynamic field10; |
| dynamic field11; |
| dynamic field12; |
| dynamic field13; |
| |
| // Prevent fields from being optimized away as write-only. |
| String toString() { |
| return field1 + |
| field2 + |
| field3 + |
| field4 + |
| field5 + |
| field6 + |
| field7 + |
| field8 + |
| field9 + |
| field10 + |
| field11 + |
| field12 + |
| field13; |
| } |
| } |
| |
| // header + 17 fields = 9 allocation units |
| class B { |
| dynamic field1; |
| dynamic field2; |
| dynamic field3; |
| dynamic field4; |
| dynamic field5; |
| dynamic field6; |
| dynamic field7; |
| dynamic field8; |
| dynamic field9; |
| dynamic field10; |
| dynamic field11; |
| dynamic field12; |
| dynamic field13; |
| dynamic field14; |
| dynamic field15; |
| dynamic field16; |
| dynamic field17; |
| |
| // Prevent fields from being optimized away as write-only. |
| String toString() { |
| return field1 + |
| field2 + |
| field3 + |
| field4 + |
| field5 + |
| field6 + |
| field7 + |
| field8 + |
| field9 + |
| field10 + |
| field11 + |
| field12 + |
| field13 + |
| field14 + |
| field15 + |
| field16 + |
| field17; |
| } |
| } |
| |
| // header + 19 fields = 10 allocation units |
| class C { |
| dynamic field1; |
| dynamic field2; |
| dynamic field3; |
| dynamic field4; |
| dynamic field5; |
| dynamic field6; |
| dynamic field7; |
| dynamic field8; |
| dynamic field9; |
| dynamic field10; |
| dynamic field11; |
| dynamic field12; |
| dynamic field13; |
| dynamic field14; |
| dynamic field15; |
| dynamic field16; |
| dynamic field17; |
| dynamic field18; |
| dynamic field19; |
| |
| // Prevent fields from being optimized away as write-only. |
| String toString() { |
| return field1 + |
| field2 + |
| field3 + |
| field4 + |
| field5 + |
| field6 + |
| field7 + |
| field8 + |
| field9 + |
| field10 + |
| field11 + |
| field12 + |
| field13 + |
| field14 + |
| field15 + |
| field16 + |
| field17 + |
| field18 + |
| field19; |
| } |
| } |
| |
| class Old { |
| dynamic next; |
| dynamic new1; |
| dynamic new2; |
| dynamic new3; |
| dynamic new4; |
| dynamic new5; |
| dynamic new6; |
| dynamic new7; |
| dynamic new8; |
| dynamic new9; |
| dynamic new10; |
| dynamic new11; |
| dynamic new12; |
| dynamic new13; |
| dynamic new14; |
| dynamic new15; |
| dynamic new16; |
| dynamic new17; |
| dynamic new18; |
| dynamic new19; |
| dynamic new20; |
| dynamic new21; |
| dynamic new22; |
| dynamic new23; |
| dynamic new24; |
| dynamic new25; |
| dynamic new26; |
| dynamic new27; |
| dynamic new28; |
| dynamic new29; |
| dynamic new30; |
| dynamic new31; |
| |
| // Prevent fields from being optimized away as write-only. |
| String toString() { |
| return new1 + |
| new2 + |
| new3 + |
| new4 + |
| new5 + |
| new6 + |
| new7 + |
| new8 + |
| new9 + |
| new10 + |
| new11 + |
| new12 + |
| new13 + |
| new14 + |
| new15 + |
| new16 + |
| new17 + |
| new18 + |
| new19 + |
| new20 + |
| new21 + |
| new22 + |
| new23 + |
| new24 + |
| new25 + |
| new26 + |
| new27 + |
| new28 + |
| new29 + |
| new30 + |
| new31; |
| } |
| } |
| |
| fill(old) { |
| // Note the allocation order is different from the field order. The objects |
| // will be scavenged in field order, causing the objects to be rearranged to |
| // produce new-space fragmentation. |
| old.new1 = new C(); |
| old.new4 = new C(); |
| old.new7 = new C(); |
| old.new10 = new C(); |
| old.new13 = new C(); |
| old.new16 = new C(); |
| |
| old.new2 = new B(); |
| old.new5 = new B(); |
| old.new8 = new B(); |
| old.new11 = new B(); |
| old.new14 = new B(); |
| old.new17 = new B(); |
| |
| old.new3 = new A(); |
| old.new6 = new A(); |
| old.new9 = new A(); |
| old.new12 = new A(); |
| old.new15 = new A(); |
| old.new18 = new A(); |
| } |
| |
| makeOld() { |
| // 2/4 MB worth of Old. |
| print("PHASE1"); |
| var head; |
| for (var i = 0; i < 16384; i++) { |
| var old = new Old(); |
| old.next = head; |
| head = old; |
| } |
| |
| // 32/64 MB worth of new objects, all directly reachable from the |
| // remembered set. |
| print("PHASE2"); |
| for (var old = head; old != null; old = old.next) { |
| fill(old); |
| } |
| |
| print("PHASE3"); |
| return head; |
| } |
| |
| main(List<String> argsIn) async { |
| if (argsIn.contains("--testee")) { |
| // Trigger OOM. |
| // Must read the fields to prevent the writes from being optimized away. If |
| // the writes are optimized away, most of the tree is collectible right away |
| // and we timeout instead of triggering OOM. |
| print(makeOld()); |
| return; |
| } |
| |
| var exec = Platform.executable; |
| var args = Platform.executableArguments + |
| [ |
| "--new_gen_semi_max_size=4" /*MB*/, |
| "--old_gen_heap_size=15" /*MB*/, |
| "--verbose_gc", |
| "--verify_store_buffer", |
| Platform.script.toFilePath(), |
| "--testee" |
| ]; |
| print("+ $exec ${args.join(' ')}"); |
| |
| var result = await Process.run(exec, args); |
| print("Command stdout:"); |
| print(result.stdout); |
| print("Command stderr:"); |
| print(result.stderr); |
| |
| Expect.equals(255, result.exitCode, |
| "Should see runtime exception error code, not SEGV"); |
| |
| Expect.isTrue( |
| result.stderr.contains("Unhandled exception:\nOut of Memory") || |
| result.stderr.contains("Unhandled exception:\r\nOut of Memory"), |
| "Should see the Dart OutOfMemoryError"); |
| |
| Expect.isFalse(result.stderr.contains("error: Out of memory"), |
| "Should not see the C++ OUT_OF_MEMORY()"); |
| } |