Skip to content

Commit e0dc78b

Browse files
aamCommit Queue
authored andcommitted
[vm/oom] If unable to create OOM stack trace, report OOM without stack trace.
Fixes flaky out_of_memory tests failures. BUG=#60453 TEST=ci Change-Id: I5c9192c6cc25660e83f6c03d4293360c353b02d1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/419583 Reviewed-by: Ryan Macnak <[email protected]> Commit-Queue: Alexander Aprelev <[email protected]>
1 parent 5ee3068 commit e0dc78b

File tree

6 files changed

+101
-38
lines changed

6 files changed

+101
-38
lines changed

runtime/vm/compiler/backend/inliner_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ void InspectStack(Dart_NativeArguments args) {
475475
EXPECT_EQ(3, frame_count);
476476
// Test something bigger than the preallocated size to verify nothing was
477477
// truncated.
478-
EXPECT(102 > StackTrace::kPreallocatedStackdepth);
478+
EXPECT(102 > StackTrace::kFixedOOMStackdepth);
479479

480480
Dart_Handle function_name;
481481
Dart_Handle script_url;

runtime/vm/dart_api_impl_test.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ TEST_CASE(DartAPI_DeepStackTraceInfo) {
520520
EXPECT_EQ(101, frame_count);
521521
// Test something bigger than the preallocated size to verify nothing was
522522
// truncated.
523-
EXPECT(101 > StackTrace::kPreallocatedStackdepth);
523+
EXPECT(101 > StackTrace::kFixedOOMStackdepth);
524524

525525
Dart_Handle function_name;
526526
Dart_Handle script_url;
@@ -598,7 +598,7 @@ void VerifyStackOverflowStackTraceInfo(const char* script,
598598
intptr_t frame_count = 0;
599599
result = Dart_StackTraceLength(stacktrace, &frame_count);
600600
EXPECT_VALID(result);
601-
EXPECT_EQ(StackTrace::kPreallocatedStackdepth - 1, frame_count);
601+
EXPECT_EQ(StackTrace::kFixedOOMStackdepth - 1, frame_count);
602602

603603
Dart_Handle function_name;
604604
Dart_Handle script_url;
@@ -693,7 +693,7 @@ void CurrentStackTraceNative(Dart_NativeArguments args) {
693693
EXPECT_EQ(102, frame_count);
694694
// Test something bigger than the preallocated size to verify nothing was
695695
// truncated.
696-
EXPECT(102 > StackTrace::kPreallocatedStackdepth);
696+
EXPECT(102 > StackTrace::kFixedOOMStackdepth);
697697

698698
Dart_Handle function_name;
699699
Dart_Handle script_url;

runtime/vm/exceptions.cc

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class StackTraceBuilder : public ValueObject {
4646
void AddFrame(const Object& code, uword pc_offset);
4747

4848
private:
49-
static constexpr int kNumTopframes = StackTrace::kPreallocatedStackdepth / 2;
49+
static constexpr int kNumTopframes = StackTrace::kFixedOOMStackdepth / 2;
5050

5151
const StackTrace& stacktrace_;
5252
intptr_t cur_index_;
@@ -56,10 +56,10 @@ class StackTraceBuilder : public ValueObject {
5656
};
5757

5858
void StackTraceBuilder::AddFrame(const Object& code, uword pc_offset) {
59-
if (cur_index_ >= StackTrace::kPreallocatedStackdepth) {
59+
if (cur_index_ >= StackTrace::kFixedOOMStackdepth) {
6060
// The number of frames is overflowing the preallocated stack trace object.
6161
Object& frame_code = Object::Handle();
62-
intptr_t start = StackTrace::kPreallocatedStackdepth - (kNumTopframes - 1);
62+
intptr_t start = StackTrace::kFixedOOMStackdepth - (kNumTopframes - 1);
6363
intptr_t null_slot = start - 2;
6464
// We are going to drop one frame.
6565
dropped_frames_++;
@@ -73,14 +73,14 @@ void StackTraceBuilder::AddFrame(const Object& code, uword pc_offset) {
7373
// Encode the number of dropped frames into the pc offset.
7474
stacktrace_.SetPcOffsetAtFrame(null_slot, dropped_frames_);
7575
// Move frames one slot down so that we can accommodate the new frame.
76-
for (intptr_t i = start; i < StackTrace::kPreallocatedStackdepth; i++) {
76+
for (intptr_t i = start; i < StackTrace::kFixedOOMStackdepth; i++) {
7777
intptr_t prev = (i - 1);
7878
frame_code = stacktrace_.CodeAtFrame(i);
7979
const uword frame_offset = stacktrace_.PcOffsetAtFrame(i);
8080
stacktrace_.SetCodeAtFrame(prev, frame_code);
8181
stacktrace_.SetPcOffsetAtFrame(prev, frame_offset);
8282
}
83-
cur_index_ = (StackTrace::kPreallocatedStackdepth - 1);
83+
cur_index_ = (StackTrace::kFixedOOMStackdepth - 1);
8484
}
8585
stacktrace_.SetCodeAtFrame(cur_index_, code);
8686
stacktrace_.SetPcOffsetAtFrame(cur_index_, pc_offset);
@@ -710,18 +710,46 @@ StackTracePtr Exceptions::CurrentStackTrace() {
710710

711711
static StackTracePtr CreateStackTrace(Zone* zone) {
712712
const Array& code_array = Array::Handle(
713-
zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
713+
zone, Array::New(StackTrace::kFixedOOMStackdepth, Heap::kOld));
714+
if (code_array.IsNull()) {
715+
return StackTrace::null();
716+
}
714717
const TypedData& pc_offset_array = TypedData::Handle(
715-
zone, TypedData::New(kUintPtrCid, StackTrace::kPreallocatedStackdepth,
716-
Heap::kOld));
718+
zone,
719+
TypedData::New(kUintPtrCid, StackTrace::kFixedOOMStackdepth, Heap::kOld));
720+
if (pc_offset_array.IsNull()) {
721+
return StackTrace::null();
722+
}
717723
const StackTrace& stack_trace =
718724
StackTrace::Handle(zone, StackTrace::New(code_array, pc_offset_array));
719725
// Expansion of inlined functions requires additional memory at run time,
720726
// avoid it.
727+
if (stack_trace.IsNull()) {
728+
return StackTrace::null();
729+
}
721730
stack_trace.set_expand_inlined(false);
722731
return stack_trace.ptr();
723732
}
724733

734+
static UnhandledExceptionPtr CreateUnhandledExceptionOrUsePrecanned(
735+
Thread* thread,
736+
const Instance& exception,
737+
const Instance& stacktrace) {
738+
UnhandledException& unhandled = UnhandledException::Handle(thread->zone());
739+
{
740+
NoThrowOOMScope no_throw_oom_scope(thread);
741+
unhandled ^= UnhandledException::New(Heap::kOld);
742+
}
743+
if (unhandled.IsNull()) {
744+
// If we failed to create new instance, use pre-canned one.
745+
unhandled ^= Object::unhandled_oom_exception().ptr();
746+
} else {
747+
unhandled.set_exception(exception);
748+
unhandled.set_stacktrace(stacktrace);
749+
}
750+
return unhandled.ptr();
751+
}
752+
725753
DART_NORETURN
726754
static void ThrowExceptionHelper(Thread* thread,
727755
const Instance& incoming_exception,
@@ -745,7 +773,7 @@ static void ThrowExceptionHelper(Thread* thread,
745773
}
746774
}
747775
#endif
748-
bool use_preallocated_stacktrace = false;
776+
bool create_stacktrace = false;
749777
Instance& exception = Instance::Handle(zone, incoming_exception.ptr());
750778
if (exception.IsNull()) {
751779
const Array& args = Array::Handle(zone, Array::New(4));
@@ -758,7 +786,7 @@ static void ThrowExceptionHelper(Thread* thread,
758786
} else if (existing_stacktrace.IsNull() &&
759787
(exception.ptr() == object_store->out_of_memory() ||
760788
exception.ptr() == object_store->stack_overflow())) {
761-
use_preallocated_stacktrace = true;
789+
create_stacktrace = true;
762790
}
763791
// Find the exception handler and determine if the handler needs a
764792
// stacktrace.
@@ -769,24 +797,32 @@ static void ThrowExceptionHelper(Thread* thread,
769797
uword handler_fp = finder.handler_fp;
770798
bool handler_needs_stacktrace = finder.needs_stacktrace;
771799
Instance& stacktrace = Instance::Handle(zone);
772-
if (use_preallocated_stacktrace) {
773-
stacktrace = CreateStackTrace(zone);
774-
if (handler_pc == 0) {
775-
// No Dart frame.
776-
ASSERT(incoming_exception.ptr() == object_store->out_of_memory());
777-
const UnhandledException& error = UnhandledException::Handle(
778-
zone, UnhandledException::New(
779-
Instance::Handle(zone, object_store->out_of_memory()),
780-
stacktrace));
781-
thread->long_jump_base()->Jump(1, error);
782-
UNREACHABLE();
800+
if (create_stacktrace) {
801+
{
802+
NoThrowOOMScope no_throw_oom_scope(thread);
803+
stacktrace = CreateStackTrace(zone);
783804
}
784-
StackTraceBuilder frame_builder(stacktrace);
785-
ASSERT(existing_stacktrace.IsNull() ||
786-
(existing_stacktrace.ptr() == stacktrace.ptr()));
787-
ASSERT(existing_stacktrace.IsNull() || is_rethrow);
788-
if (handler_needs_stacktrace && existing_stacktrace.IsNull()) {
789-
BuildStackTrace(&frame_builder);
805+
// Ensure we have enough memory to create stacktrace,
806+
// otherwise fallback to reporting OOM without stacktrace.
807+
if (!stacktrace.IsNull()) {
808+
if (handler_pc == 0) {
809+
// No Dart frame.
810+
ASSERT(incoming_exception.ptr() == object_store->out_of_memory());
811+
UnhandledException& error = UnhandledException::Handle(
812+
zone,
813+
CreateUnhandledExceptionOrUsePrecanned(
814+
thread, Instance::Handle(zone, object_store->out_of_memory()),
815+
stacktrace));
816+
thread->long_jump_base()->Jump(1, error);
817+
UNREACHABLE();
818+
}
819+
StackTraceBuilder frame_builder(stacktrace);
820+
ASSERT(existing_stacktrace.IsNull() ||
821+
(existing_stacktrace.ptr() == stacktrace.ptr()));
822+
ASSERT(existing_stacktrace.IsNull() || is_rethrow);
823+
if (handler_needs_stacktrace && existing_stacktrace.IsNull()) {
824+
BuildStackTrace(&frame_builder);
825+
}
790826
}
791827
} else {
792828
if (!existing_stacktrace.IsNull()) {
@@ -839,7 +875,8 @@ static void ThrowExceptionHelper(Thread* thread,
839875
// the isolate etc.). This can happen in the compiler, which is not
840876
// allowed to allocate in new space, so we pass the kOld argument.
841877
const UnhandledException& unhandled_exception = UnhandledException::Handle(
842-
zone, UnhandledException::New(exception, stacktrace, Heap::kOld));
878+
zone,
879+
CreateUnhandledExceptionOrUsePrecanned(thread, exception, stacktrace));
843880
stacktrace = StackTrace::null();
844881
JumpToExceptionHandler(thread, handler_pc, handler_sp, handler_fp,
845882
unhandled_exception, stacktrace);

runtime/vm/object.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,8 @@ void Object::Init(IsolateGroup* isolate_group) {
12831283
error_str = String::New("Out of memory", Heap::kOld);
12841284
*out_of_memory_error_ =
12851285
LanguageError::New(error_str, Report::kError, Heap::kOld);
1286+
*unhandled_oom_exception_ =
1287+
UnhandledException::New(error_str, StackTrace::Handle(), Heap::kOld);
12861288

12871289
// Allocate the parameter types and names for synthetic getters.
12881290
*synthetic_getter_parameter_types_ = Array::New(1, Heap::kOld);
@@ -1407,6 +1409,8 @@ void Object::Init(IsolateGroup* isolate_group) {
14071409
ASSERT(background_compilation_error_->IsLanguageError());
14081410
ASSERT(!out_of_memory_error_->IsSmi());
14091411
ASSERT(out_of_memory_error_->IsLanguageError());
1412+
ASSERT(!unhandled_oom_exception_->IsSmi());
1413+
ASSERT(unhandled_oom_exception_->IsUnhandledException());
14101414
ASSERT(!vm_isolate_snapshot_object_table_->IsSmi());
14111415
ASSERT(vm_isolate_snapshot_object_table_->IsArray());
14121416
ASSERT(!synthetic_getter_parameter_types_->IsSmi());
@@ -2887,6 +2891,9 @@ ObjectPtr Object::Allocate(intptr_t cls_id,
28872891
Report::LongJump(Object::out_of_memory_error());
28882892
UNREACHABLE();
28892893
} else if (thread->top_exit_frame_info() != 0) {
2894+
if (thread->IsInNoThrowOOMScope()) {
2895+
return Object::null();
2896+
}
28902897
Exceptions::ThrowOOM();
28912898
UNREACHABLE();
28922899
} else {

runtime/vm/object.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ class Object {
515515
V(LanguageError, background_compilation_error) \
516516
V(LanguageError, no_debuggable_code_error) \
517517
V(LanguageError, out_of_memory_error) \
518+
V(UnhandledException, unhandled_oom_exception) \
518519
V(Array, vm_isolate_snapshot_object_table) \
519520
V(Type, dynamic_type) \
520521
V(Type, void_type) \
@@ -8246,11 +8247,13 @@ class LanguageError : public Error {
82468247
class UnhandledException : public Error {
82478248
public:
82488249
InstancePtr exception() const { return untag()->exception(); }
8250+
void set_exception(const Instance& exception) const;
82498251
static intptr_t exception_offset() {
82508252
return OFFSET_OF(UntaggedUnhandledException, exception_);
82518253
}
82528254

82538255
InstancePtr stacktrace() const { return untag()->stacktrace(); }
8256+
void set_stacktrace(const Instance& stacktrace) const;
82548257
static intptr_t stacktrace_offset() {
82558258
return OFFSET_OF(UntaggedUnhandledException, stacktrace_);
82568259
}
@@ -8262,15 +8265,11 @@ class UnhandledException : public Error {
82628265
static UnhandledExceptionPtr New(const Instance& exception,
82638266
const Instance& stacktrace,
82648267
Heap::Space space = Heap::kNew);
8268+
static UnhandledExceptionPtr New(Heap::Space space = Heap::kNew);
82658269

82668270
virtual const char* ToErrorCString() const;
82678271

82688272
private:
8269-
static UnhandledExceptionPtr New(Heap::Space space = Heap::kNew);
8270-
8271-
void set_exception(const Instance& exception) const;
8272-
void set_stacktrace(const Instance& stacktrace) const;
8273-
82748273
FINAL_HEAP_OBJECT_IMPLEMENTATION(UnhandledException, Error);
82758274
friend class Class;
82768275
friend class ObjectStore;
@@ -12620,7 +12619,7 @@ class DebuggerStackTrace;
1262012619
// Internal stacktrace object used in exceptions for printing stack traces.
1262112620
class StackTrace : public Instance {
1262212621
public:
12623-
static constexpr int kPreallocatedStackdepth = 90;
12622+
static constexpr int kFixedOOMStackdepth = 90;
1262412623

1262512624
intptr_t Length() const;
1262612625

runtime/vm/thread.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,8 @@ class Thread : public ThreadState {
759759
return stopped_mutators_scope_depth_ > 0;
760760
}
761761

762+
bool IsInNoThrowOOMScope() const { return no_throw_oom_scope_depth_ > 0; }
763+
762764
#define DEFINE_OFFSET_METHOD(type_name, member_name, expr, default_init_value) \
763765
static intptr_t member_name##offset() { \
764766
return OFFSET_OF(Thread, member_name); \
@@ -1437,6 +1439,7 @@ class Thread : public ThreadState {
14371439
VMHandles reusable_handles_;
14381440
int32_t stack_overflow_count_;
14391441
uint32_t runtime_call_count_ = 0;
1442+
intptr_t no_throw_oom_scope_depth_ = 0;
14401443

14411444
// Deoptimization of stack frames.
14421445
RuntimeCallDeoptAbility runtime_call_deopt_ability_ =
@@ -1610,6 +1613,7 @@ class Thread : public ThreadState {
16101613
friend class Simulator;
16111614
friend class StackZone;
16121615
friend class StoppedMutatorsScope;
1616+
friend class NoThrowOOMScope;
16131617
friend class ThreadRegistry;
16141618
friend class CompilerState;
16151619
friend class compiler::target::Thread;
@@ -1838,6 +1842,22 @@ class LeaveCompilerScope : public ValueObject {
18381842
};
18391843
#endif // defined(DEBUG)
18401844

1845+
class NoThrowOOMScope : public ThreadStackResource {
1846+
public:
1847+
explicit NoThrowOOMScope(Thread* thread) : ThreadStackResource(thread) {
1848+
thread->no_throw_oom_scope_depth_++;
1849+
ASSERT(thread->no_throw_oom_scope_depth_ >= 0);
1850+
}
1851+
1852+
~NoThrowOOMScope() {
1853+
thread()->no_throw_oom_scope_depth_ -= 1;
1854+
ASSERT(thread()->no_throw_oom_scope_depth_ >= 0);
1855+
}
1856+
1857+
private:
1858+
DISALLOW_COPY_AND_ASSIGN(NoThrowOOMScope);
1859+
};
1860+
18411861
} // namespace dart
18421862

18431863
#endif // RUNTIME_VM_THREAD_H_

0 commit comments

Comments
 (0)