|  | // Copyright (c) 2017, 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. | 
|  |  | 
|  | #include "lib/stacktrace.h" | 
|  | #include "vm/compiler/assembler/disassembler.h" | 
|  | #include "vm/heap/safepoint.h" | 
|  | #include "vm/object.h" | 
|  | #include "vm/os_thread.h" | 
|  | #include "vm/stack_frame.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | #if !defined(PRODUCT) | 
|  |  | 
|  | #if defined(__GNUC__) | 
|  | // Older toolchains don't know about "retain" | 
|  | #pragma GCC diagnostic ignored "-Wattributes" | 
|  | #define GDB_HELPER extern "C" __attribute__((used, retain)) | 
|  | #else | 
|  | #define GDB_HELPER extern "C" | 
|  | #endif | 
|  |  | 
|  | GDB_HELPER | 
|  | void* _currentThread() { | 
|  | return OSThread::CurrentVMThread(); | 
|  | } | 
|  |  | 
|  | GDB_HELPER | 
|  | void _printObjectPtr(uword object) { | 
|  | OS::PrintErr("%s\n", | 
|  | Object::Handle(static_cast<ObjectPtr>(object)).ToCString()); | 
|  | } | 
|  |  | 
|  | GDB_HELPER | 
|  | Object* _handle(uword object) { | 
|  | return &Object::Handle(static_cast<ObjectPtr>(object)); | 
|  | } | 
|  |  | 
|  | GDB_HELPER | 
|  | void _disassemble(uword pc) { | 
|  | Code& code = Code::Handle(Code::FindCodeUnsafe(pc)); | 
|  | if (code.IsNull()) { | 
|  | OS::PrintErr("No code found\n"); | 
|  | } else { | 
|  | Object& owner = Object::Handle(code.owner()); | 
|  | if (owner.IsFunction()) { | 
|  | Disassembler::DisassembleCode(Function::Cast(owner), code, | 
|  | code.is_optimized()); | 
|  | } else { | 
|  | Disassembler::DisassembleStub(code.Name(), code); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // An utility method for convenient printing of dart stack traces when | 
|  | // inside 'gdb'. Note: This function will only work when there is a | 
|  | // valid exit frame information. It will not work when a breakpoint is | 
|  | // set in dart code and control is got inside 'gdb' without going through | 
|  | // the runtime or native transition stub. | 
|  | GDB_HELPER | 
|  | void _printDartStackTrace() { | 
|  | const StackTrace& stacktrace = GetCurrentStackTrace(0); | 
|  | OS::PrintErr("=== Current Trace:\n%s===\n", stacktrace.ToCString()); | 
|  | } | 
|  |  | 
|  | // Like _printDartStackTrace, but works in a NoSafepointScope. Use it if you're | 
|  | // in the middle of a GC or interested in stub frames. | 
|  | GDB_HELPER | 
|  | void _printStackTrace() { | 
|  | StackFrame::DumpCurrentTrace(); | 
|  | } | 
|  |  | 
|  | // Like _printDartStackTrace, but works when stopped in generated code. | 
|  | // Must be called with the current fp, sp, and pc. I.e., | 
|  | // | 
|  | // (gdb) print _printGeneratedStackTrace($rbp, $rsp, $rip) | 
|  | GDB_HELPER | 
|  | void _printGeneratedStackTrace(uword fp, uword sp, uword pc) { | 
|  | StackFrameIterator frames(fp, sp, pc, ValidationPolicy::kDontValidateFrames, | 
|  | Thread::Current(), | 
|  | StackFrameIterator::kNoCrossThreadIteration); | 
|  | StackFrame* frame = frames.NextFrame(); | 
|  | while (frame != nullptr) { | 
|  | OS::PrintErr("%s\n", frame->ToCString()); | 
|  | frame = frames.NextFrame(); | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(DART_DYNAMIC_MODULES) | 
|  | // Like _printDartStackTrace, but works in the interpreter loop. | 
|  | // Must be called with the current interpreter fp, sp, and pc. | 
|  | // Note that sp[0] is not modified, but sp[1] will be trashed. | 
|  | DART_EXPORT | 
|  | void _printInterpreterStackTrace(ObjectPtr* fp, | 
|  | ObjectPtr* sp, | 
|  | const KBCInstr* pc) { | 
|  | Thread* thread = Thread::Current(); | 
|  | sp[1] = Function::null(); | 
|  | sp[2] = Bytecode::null(); | 
|  | sp[3] = static_cast<ObjectPtr>(reinterpret_cast<uword>(pc)); | 
|  | sp[4] = static_cast<ObjectPtr>(reinterpret_cast<uword>(fp)); | 
|  | ObjectPtr* exit_fp = sp + 1 + kKBCDartFrameFixedSize; | 
|  | thread->set_top_exit_frame_info(reinterpret_cast<uword>(exit_fp)); | 
|  | thread->set_execution_state(Thread::kThreadInVM); | 
|  | _printDartStackTrace(); | 
|  | thread->set_execution_state(Thread::kThreadInGenerated); | 
|  | thread->set_top_exit_frame_info(0); | 
|  | } | 
|  | #endif  // defined(DART_DYNAMIC_MODULES) | 
|  |  | 
|  | class PrintObjectPointersVisitor : public ObjectPointerVisitor { | 
|  | public: | 
|  | PrintObjectPointersVisitor() | 
|  | : ObjectPointerVisitor(IsolateGroup::Current()) {} | 
|  |  | 
|  | void VisitPointers(ObjectPtr* first, ObjectPtr* last) override { | 
|  | for (ObjectPtr* p = first; p <= last; p++) { | 
|  | Object& obj = Object::Handle(*p); | 
|  | OS::PrintErr("%p: %s\n", p, obj.ToCString()); | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(DART_COMPRESSED_POINTERS) | 
|  | void VisitCompressedPointers(uword heap_base, | 
|  | CompressedObjectPtr* first, | 
|  | CompressedObjectPtr* last) override { | 
|  | for (CompressedObjectPtr* p = first; p <= last; p++) { | 
|  | Object& obj = Object::Handle(p->Decompress(heap_base)); | 
|  | OS::PrintErr("%p: %s\n", p, obj.ToCString()); | 
|  | } | 
|  | } | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | GDB_HELPER | 
|  | void _printStackTraceWithLocals() { | 
|  | PrintObjectPointersVisitor visitor; | 
|  | StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, | 
|  | Thread::Current(), | 
|  | StackFrameIterator::kNoCrossThreadIteration); | 
|  | StackFrame* frame = frames.NextFrame(); | 
|  | while (frame != nullptr) { | 
|  | OS::PrintErr("%s\n", frame->ToCString()); | 
|  | frame->VisitObjectPointers(&visitor); | 
|  | frame = frames.NextFrame(); | 
|  | } | 
|  | } | 
|  |  | 
|  | #endif  // !PRODUCT | 
|  |  | 
|  | }  // namespace dart |