OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/exceptions.h" | 5 #include "vm/exceptions.h" |
6 | 6 |
7 #include "vm/dart_api_impl.h" | 7 #include "vm/dart_api_impl.h" |
8 #include "vm/dart_entry.h" | 8 #include "vm/dart_entry.h" |
9 #include "vm/debugger.h" | 9 #include "vm/debugger.h" |
10 #include "vm/flags.h" | 10 #include "vm/flags.h" |
11 #include "vm/object.h" | 11 #include "vm/object.h" |
12 #include "vm/object_store.h" | 12 #include "vm/object_store.h" |
13 #include "vm/stack_frame.h" | 13 #include "vm/stack_frame.h" |
14 #include "vm/stub_code.h" | 14 #include "vm/stub_code.h" |
15 #include "vm/symbols.h" | 15 #include "vm/symbols.h" |
16 | 16 |
17 namespace dart { | 17 namespace dart { |
18 | 18 |
19 DEFINE_FLAG(bool, print_stacktrace_at_throw, false, | 19 DEFINE_FLAG(bool, print_stacktrace_at_throw, false, |
20 "Prints a stack trace everytime a throw occurs."); | 20 "Prints a stack trace everytime a throw occurs."); |
21 DEFINE_FLAG(bool, heap_profile_out_of_memory, false, | 21 DEFINE_FLAG(bool, heap_profile_out_of_memory, false, |
22 "Writes a heap profile on unhandled out-of-memory exceptions."); | 22 "Writes a heap profile on unhandled out-of-memory exceptions."); |
23 DEFINE_FLAG(bool, verbose_stacktrace, false, | 23 DEFINE_FLAG(bool, verbose_stacktrace, false, |
24 "Stack traces will include methods marked invisible."); | 24 "Stack traces will include methods marked invisible."); |
25 | 25 |
26 const char* Exceptions::kCastErrorDstName = "type cast"; | 26 const char* Exceptions::kCastErrorDstName = "type cast"; |
27 | 27 |
28 | 28 |
| 29 class StacktraceBuilder : public ValueObject { |
| 30 public: |
| 31 StacktraceBuilder() { } |
| 32 virtual ~StacktraceBuilder() { } |
| 33 |
| 34 virtual void AddFrame(const Function& func, |
| 35 const Code& code, |
| 36 const Smi& offset) = 0; |
| 37 }; |
| 38 |
| 39 |
| 40 class RegularStacktraceBuilder : public StacktraceBuilder { |
| 41 public: |
| 42 RegularStacktraceBuilder() |
| 43 : func_list_(GrowableObjectArray::Handle(GrowableObjectArray::New())), |
| 44 code_list_(GrowableObjectArray::Handle(GrowableObjectArray::New())), |
| 45 pc_offset_list_( |
| 46 GrowableObjectArray::Handle(GrowableObjectArray::New())) { } |
| 47 ~RegularStacktraceBuilder() { } |
| 48 |
| 49 const GrowableObjectArray& func_list() const { return func_list_; } |
| 50 const GrowableObjectArray& code_list() const { return code_list_; } |
| 51 const GrowableObjectArray& pc_offset_list() const { return pc_offset_list_; } |
| 52 |
| 53 virtual void AddFrame(const Function& func, |
| 54 const Code& code, |
| 55 const Smi& offset) { |
| 56 func_list_.Add(func); |
| 57 code_list_.Add(code); |
| 58 pc_offset_list_.Add(offset); |
| 59 } |
| 60 |
| 61 private: |
| 62 const GrowableObjectArray& func_list_; |
| 63 const GrowableObjectArray& code_list_; |
| 64 const GrowableObjectArray& pc_offset_list_; |
| 65 |
| 66 DISALLOW_COPY_AND_ASSIGN(RegularStacktraceBuilder); |
| 67 }; |
| 68 |
| 69 |
| 70 class PreallocatedStacktraceBuilder : public StacktraceBuilder { |
| 71 public: |
| 72 explicit PreallocatedStacktraceBuilder(const Stacktrace& stacktrace) |
| 73 : stacktrace_(stacktrace), |
| 74 cur_index_(0) { |
| 75 ASSERT(stacktrace_.raw() == |
| 76 Isolate::Current()->object_store()->preallocated_stack_trace()); |
| 77 } |
| 78 ~PreallocatedStacktraceBuilder() { } |
| 79 |
| 80 virtual void AddFrame(const Function& func, |
| 81 const Code& code, |
| 82 const Smi& offset); |
| 83 |
| 84 private: |
| 85 static const int kNumTopframes = 3; |
| 86 |
| 87 const Stacktrace& stacktrace_; |
| 88 intptr_t cur_index_; |
| 89 |
| 90 DISALLOW_COPY_AND_ASSIGN(PreallocatedStacktraceBuilder); |
| 91 }; |
| 92 |
| 93 |
| 94 void PreallocatedStacktraceBuilder::AddFrame(const Function& func, |
| 95 const Code& code, |
| 96 const Smi& offset) { |
| 97 if (cur_index_ >= Stacktrace::kPreallocatedStackdepth) { |
| 98 // The number of frames is overflowing the preallocated stack trace object. |
| 99 Function& frame_func = Function::Handle(); |
| 100 Code& frame_code = Code::Handle(); |
| 101 Smi& frame_offset = Smi::Handle(); |
| 102 intptr_t start = Stacktrace::kPreallocatedStackdepth - (kNumTopframes - 1); |
| 103 intptr_t null_slot = start - 2; |
| 104 // Add an empty slot to indicate the overflow so that the toString |
| 105 // method can account for the overflow. |
| 106 if (stacktrace_.FunctionAtFrame(null_slot) != Function::null()) { |
| 107 stacktrace_.SetFunctionAtFrame(null_slot, frame_func); |
| 108 stacktrace_.SetCodeAtFrame(null_slot, frame_code); |
| 109 } |
| 110 // Move frames one slot down so that we can accomadate the new frame. |
| 111 for (intptr_t i = start; i < Stacktrace::kPreallocatedStackdepth; i++) { |
| 112 intptr_t prev = (i - 1); |
| 113 frame_func = stacktrace_.FunctionAtFrame(i); |
| 114 frame_code = stacktrace_.CodeAtFrame(i); |
| 115 frame_offset = stacktrace_.PcOffsetAtFrame(i); |
| 116 stacktrace_.SetFunctionAtFrame(prev, frame_func); |
| 117 stacktrace_.SetCodeAtFrame(prev, frame_code); |
| 118 stacktrace_.SetPcOffsetAtFrame(prev, frame_offset); |
| 119 } |
| 120 cur_index_ = (Stacktrace::kPreallocatedStackdepth - 1); |
| 121 } |
| 122 stacktrace_.SetFunctionAtFrame(cur_index_, func); |
| 123 stacktrace_.SetCodeAtFrame(cur_index_, code); |
| 124 stacktrace_.SetPcOffsetAtFrame(cur_index_, offset); |
| 125 cur_index_ += 1; |
| 126 } |
| 127 |
| 128 |
29 static bool ShouldShowFunction(const Function& function) { | 129 static bool ShouldShowFunction(const Function& function) { |
30 if (FLAG_verbose_stacktrace) { | 130 if (FLAG_verbose_stacktrace) { |
31 return true; | 131 return true; |
32 } | 132 } |
33 return function.is_visible(); | 133 return function.is_visible(); |
34 } | 134 } |
35 | 135 |
36 | 136 |
37 // Iterate through the stack frames and try to find a frame with an | 137 // Iterate through the stack frames and try to find a frame with an |
38 // exception handler. Once found, set the pc, sp and fp so that execution | 138 // exception handler. Once found, set the pc, sp and fp so that execution |
39 // can continue in that frame. | 139 // can continue in that frame. |
40 static bool FindExceptionHandler(uword* handler_pc, | 140 static bool FindExceptionHandler(uword* handler_pc, |
41 uword* handler_sp, | 141 uword* handler_sp, |
42 uword* handler_fp, | 142 uword* handler_fp, |
43 const GrowableObjectArray& func_list, | 143 StacktraceBuilder* builder) { |
44 const GrowableObjectArray& code_list, | |
45 const GrowableObjectArray& pc_offset_list) { | |
46 StackFrameIterator frames(StackFrameIterator::kDontValidateFrames); | 144 StackFrameIterator frames(StackFrameIterator::kDontValidateFrames); |
47 StackFrame* frame = frames.NextFrame(); | 145 StackFrame* frame = frames.NextFrame(); |
48 ASSERT(frame != NULL); // We expect to find a dart invocation frame. | 146 ASSERT(frame != NULL); // We expect to find a dart invocation frame. |
49 Function& func = Function::Handle(); | 147 Function& func = Function::Handle(); |
50 Code& code = Code::Handle(); | 148 Code& code = Code::Handle(); |
51 Smi& offset = Smi::Handle(); | 149 Smi& offset = Smi::Handle(); |
52 while (!frame->IsEntryFrame()) { | 150 while (!frame->IsEntryFrame()) { |
53 if (frame->IsDartFrame()) { | 151 if (frame->IsDartFrame()) { |
54 code = frame->LookupDartCode(); | 152 code = frame->LookupDartCode(); |
55 if (code.is_optimized()) { | 153 if (code.is_optimized()) { |
56 // For optimized frames, extract all the inlined functions if any | 154 // For optimized frames, extract all the inlined functions if any |
57 // into the stack trace. | 155 // into the stack trace. |
58 for (InlinedFunctionsIterator it(frame); !it.Done(); it.Advance()) { | 156 for (InlinedFunctionsIterator it(frame); !it.Done(); it.Advance()) { |
59 func = it.function(); | 157 func = it.function(); |
60 code = it.code(); | 158 code = it.code(); |
61 uword pc = it.pc(); | 159 uword pc = it.pc(); |
62 ASSERT(pc != 0); | 160 ASSERT(pc != 0); |
63 ASSERT(code.EntryPoint() <= pc); | 161 ASSERT(code.EntryPoint() <= pc); |
64 ASSERT(pc < (code.EntryPoint() + code.Size())); | 162 ASSERT(pc < (code.EntryPoint() + code.Size())); |
65 if (ShouldShowFunction(func)) { | 163 if (ShouldShowFunction(func)) { |
66 offset = Smi::New(pc - code.EntryPoint()); | 164 offset = Smi::New(pc - code.EntryPoint()); |
67 func_list.Add(func); | 165 builder->AddFrame(func, code, offset); |
68 code_list.Add(code); | |
69 pc_offset_list.Add(offset); | |
70 } | 166 } |
71 } | 167 } |
72 } else { | 168 } else { |
73 offset = Smi::New(frame->pc() - code.EntryPoint()); | 169 offset = Smi::New(frame->pc() - code.EntryPoint()); |
74 func = code.function(); | 170 func = code.function(); |
75 if (ShouldShowFunction(func)) { | 171 if (ShouldShowFunction(func)) { |
76 func_list.Add(func); | 172 builder->AddFrame(func, code, offset); |
77 code_list.Add(code); | |
78 pc_offset_list.Add(offset); | |
79 } | 173 } |
80 } | 174 } |
81 if (frame->FindExceptionHandler(handler_pc)) { | 175 if (frame->FindExceptionHandler(handler_pc)) { |
82 *handler_sp = frame->sp(); | 176 *handler_sp = frame->sp(); |
83 *handler_fp = frame->fp(); | 177 *handler_fp = frame->fp(); |
84 return true; | 178 return true; |
85 } | 179 } |
86 } | 180 } |
87 frame = frames.NextFrame(); | 181 frame = frames.NextFrame(); |
88 ASSERT(frame != NULL); | 182 ASSERT(frame != NULL); |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 typedef void (*ErrorHandler)(uword, uword, uword, RawError*); | 258 typedef void (*ErrorHandler)(uword, uword, uword, RawError*); |
165 ErrorHandler func = reinterpret_cast<ErrorHandler>( | 259 ErrorHandler func = reinterpret_cast<ErrorHandler>( |
166 StubCode::JumpToErrorHandlerEntryPoint()); | 260 StubCode::JumpToErrorHandlerEntryPoint()); |
167 func(program_counter, stack_pointer, frame_pointer, raw_error); | 261 func(program_counter, stack_pointer, frame_pointer, raw_error); |
168 UNREACHABLE(); | 262 UNREACHABLE(); |
169 } | 263 } |
170 | 264 |
171 | 265 |
172 static void ThrowExceptionHelper(const Instance& incoming_exception, | 266 static void ThrowExceptionHelper(const Instance& incoming_exception, |
173 const Instance& existing_stacktrace) { | 267 const Instance& existing_stacktrace) { |
174 Instance& exception = Instance::Handle(incoming_exception.raw()); | 268 bool use_preallocated_stacktrace = false; |
| 269 Isolate* isolate = Isolate::Current(); |
| 270 Instance& exception = Instance::Handle(isolate, incoming_exception.raw()); |
175 if (exception.IsNull()) { | 271 if (exception.IsNull()) { |
176 exception ^= Exceptions::Create(Exceptions::kNullThrown, | 272 exception ^= Exceptions::Create(Exceptions::kNullThrown, |
177 Object::empty_array()); | 273 Object::empty_array()); |
| 274 } else if (exception.raw() == isolate->object_store()->out_of_memory() || |
| 275 exception.raw() == isolate->object_store()->stack_overflow()) { |
| 276 use_preallocated_stacktrace = true; |
178 } | 277 } |
179 uword handler_pc = 0; | 278 uword handler_pc = 0; |
180 uword handler_sp = 0; | 279 uword handler_sp = 0; |
181 uword handler_fp = 0; | 280 uword handler_fp = 0; |
182 const GrowableObjectArray& func_list = | 281 Stacktrace& stacktrace = Stacktrace::Handle(isolate); |
183 GrowableObjectArray::Handle(GrowableObjectArray::New()); | 282 bool handler_exists = false; |
184 const GrowableObjectArray& code_list = | 283 if (use_preallocated_stacktrace) { |
185 GrowableObjectArray::Handle(GrowableObjectArray::New()); | 284 stacktrace ^= isolate->object_store()->preallocated_stack_trace(); |
186 const GrowableObjectArray& pc_offset_list = | 285 PreallocatedStacktraceBuilder frame_builder(stacktrace); |
187 GrowableObjectArray::Handle(GrowableObjectArray::New()); | 286 handler_exists = FindExceptionHandler(&handler_pc, |
188 bool handler_exists = FindExceptionHandler(&handler_pc, | 287 &handler_sp, |
189 &handler_sp, | 288 &handler_fp, |
190 &handler_fp, | 289 &frame_builder); |
191 func_list, | 290 } else { |
192 code_list, | 291 RegularStacktraceBuilder frame_builder; |
193 pc_offset_list); | 292 handler_exists = FindExceptionHandler(&handler_pc, |
| 293 &handler_sp, |
| 294 &handler_fp, |
| 295 &frame_builder); |
| 296 // TODO(5411263): At some point we can optimize by figuring out if a |
| 297 // stack trace is needed based on whether the catch code specifies a |
| 298 // stack trace object or there is a rethrow in the catch clause. |
| 299 if (frame_builder.pc_offset_list().Length() != 0) { |
| 300 // Create arrays for function, code and pc_offset triplet for each frame. |
| 301 const Array& func_array = |
| 302 Array::Handle(isolate, Array::MakeArray(frame_builder.func_list())); |
| 303 const Array& code_array = |
| 304 Array::Handle(isolate, Array::MakeArray(frame_builder.code_list())); |
| 305 const Array& pc_offset_array = |
| 306 Array::Handle(isolate, |
| 307 Array::MakeArray(frame_builder.pc_offset_list())); |
| 308 if (existing_stacktrace.IsNull()) { |
| 309 stacktrace = Stacktrace::New(func_array, code_array, pc_offset_array); |
| 310 } else { |
| 311 stacktrace ^= existing_stacktrace.raw(); |
| 312 stacktrace.Append(func_array, code_array, pc_offset_array); |
| 313 } |
| 314 } else { |
| 315 stacktrace ^= existing_stacktrace.raw(); |
| 316 } |
| 317 } |
194 // We expect to find a handler_pc, if the exception is unhandled | 318 // We expect to find a handler_pc, if the exception is unhandled |
195 // then we expect to at least have the dart entry frame on the | 319 // then we expect to at least have the dart entry frame on the |
196 // stack as Exceptions::Throw should happen only after a dart | 320 // stack as Exceptions::Throw should happen only after a dart |
197 // invocation has been done. | 321 // invocation has been done. |
198 ASSERT(handler_pc != 0); | 322 ASSERT(handler_pc != 0); |
199 | 323 |
200 // TODO(5411263): At some point we can optimize by figuring out if a | |
201 // stack trace is needed based on whether the catch code specifies a | |
202 // stack trace object or there is a rethrow in the catch clause. | |
203 Stacktrace& stacktrace = Stacktrace::Handle(); | |
204 if (pc_offset_list.Length() != 0) { | |
205 if (existing_stacktrace.IsNull()) { | |
206 stacktrace = Stacktrace::New(func_list, code_list, pc_offset_list); | |
207 } else { | |
208 stacktrace ^= existing_stacktrace.raw(); | |
209 stacktrace.Append(func_list, code_list, pc_offset_list); | |
210 } | |
211 } else { | |
212 stacktrace ^= existing_stacktrace.raw(); | |
213 } | |
214 if (FLAG_print_stacktrace_at_throw) { | 324 if (FLAG_print_stacktrace_at_throw) { |
215 OS::Print("Exception '%s' thrown:\n", exception.ToCString()); | 325 OS::Print("Exception '%s' thrown:\n", exception.ToCString()); |
216 OS::Print("%s\n", stacktrace.ToCString()); | 326 OS::Print("%s\n", stacktrace.ToCString()); |
217 } | 327 } |
218 if (handler_exists) { | 328 if (handler_exists) { |
219 // Found a dart handler for the exception, jump to it. | 329 // Found a dart handler for the exception, jump to it. |
220 JumpToExceptionHandler(handler_pc, | 330 JumpToExceptionHandler(handler_pc, |
221 handler_sp, | 331 handler_sp, |
222 handler_fp, | 332 handler_fp, |
223 exception, | 333 exception, |
224 stacktrace); | 334 stacktrace); |
225 } else { | 335 } else { |
226 if (FLAG_heap_profile_out_of_memory) { | 336 if (FLAG_heap_profile_out_of_memory) { |
227 Isolate* isolate = Isolate::Current(); | |
228 if (exception.raw() == isolate->object_store()->out_of_memory()) { | 337 if (exception.raw() == isolate->object_store()->out_of_memory()) { |
229 isolate->heap()->ProfileToFile("out-of-memory"); | 338 isolate->heap()->ProfileToFile("out-of-memory"); |
230 } | 339 } |
231 } | 340 } |
232 // No dart exception handler found in this invocation sequence, | 341 // No dart exception handler found in this invocation sequence, |
233 // so we create an unhandled exception object and return to the | 342 // so we create an unhandled exception object and return to the |
234 // invocation stub so that it returns this unhandled exception | 343 // invocation stub so that it returns this unhandled exception |
235 // object. The C++ code which invoked this dart sequence can check | 344 // object. The C++ code which invoked this dart sequence can check |
236 // and do the appropriate thing (rethrow the exception to the | 345 // and do the appropriate thing (rethrow the exception to the |
237 // dart invocation sequence above it, print diagnostics and terminate | 346 // dart invocation sequence above it, print diagnostics and terminate |
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
480 break; | 589 break; |
481 } | 590 } |
482 | 591 |
483 return DartLibraryCalls::ExceptionCreate(library, | 592 return DartLibraryCalls::ExceptionCreate(library, |
484 *class_name, | 593 *class_name, |
485 *constructor_name, | 594 *constructor_name, |
486 arguments); | 595 arguments); |
487 } | 596 } |
488 | 597 |
489 } // namespace dart | 598 } // namespace dart |
OLD | NEW |