| // Copyright 2018 The Fuchsia Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "src/developer/debug/zxdb/console/format_frame.h" | 
 |  | 
 | #include <inttypes.h> | 
 | #include <lib/syslog/cpp/macros.h> | 
 |  | 
 | #include "src/developer/debug/zxdb/client/frame.h" | 
 | #include "src/developer/debug/zxdb/client/process.h" | 
 | #include "src/developer/debug/zxdb/client/thread.h" | 
 | #include "src/developer/debug/zxdb/console/command_utils.h" | 
 | #include "src/developer/debug/zxdb/console/console.h" | 
 | #include "src/developer/debug/zxdb/console/format_location.h" | 
 | #include "src/developer/debug/zxdb/console/format_node_console.h" | 
 | #include "src/developer/debug/zxdb/console/output_buffer.h" | 
 | #include "src/developer/debug/zxdb/console/string_util.h" | 
 | #include "src/developer/debug/zxdb/symbols/function.h" | 
 | #include "src/developer/debug/zxdb/symbols/location.h" | 
 | #include "src/developer/debug/zxdb/symbols/value.h" | 
 | #include "src/lib/fxl/strings/string_printf.h" | 
 |  | 
 | namespace zxdb { | 
 |  | 
 | namespace { | 
 |  | 
 | // Appends the frame heading (indent, active marker, frame number) for a stack entry. The frame | 
 | // numbers are expressed in a range (inclusive) to support pretty-printing. They should be the same | 
 | // for normal stack entries. | 
 | void AppendFrameNumber(size_t begin_range, size_t end_range, size_t active_frame_index, | 
 |                        AsyncOutputBuffer* out) { | 
 |   if (active_frame_index >= begin_range && active_frame_index <= end_range) | 
 |     out->Append(GetCurrentRowMarker() + " "); | 
 |   else | 
 |     out->Append("  "); | 
 |  | 
 |   if (begin_range == end_range) { | 
 |     out->Append(OutputBuffer(Syntax::kSpecial, fxl::StringPrintf("%zu ", begin_range))); | 
 |   } else { | 
 |     out->Append(OutputBuffer(Syntax::kSpecial, fxl::StringPrintf("%zu", begin_range))); | 
 |     out->Append(Syntax::kComment, "…"); | 
 |     out->Append(OutputBuffer(Syntax::kSpecial, fxl::StringPrintf("%zu ", end_range))); | 
 |   } | 
 | } | 
 |  | 
 | fxl::RefPtr<AsyncOutputBuffer> ListCompletedFrames(Thread* thread, const FormatStackOptions& opts) { | 
 |   size_t active_frame_id = | 
 |       static_cast<size_t>(Console::get()->context().GetActiveFrameIdForThread(thread)); | 
 |  | 
 |   auto out = fxl::MakeRefCounted<AsyncOutputBuffer>(); | 
 |  | 
 |   // This doesn't use table output since the format of the stack frames is usually so unpredictable. | 
 |   const Stack& stack = thread->GetStack(); | 
 |   if (stack.empty()) { | 
 |     if (thread->GetState() != debug_ipc::ThreadRecord::State::kSuspended && | 
 |         !(thread->GetState() == debug_ipc::ThreadRecord::State::kBlocked && | 
 |           thread->GetBlockedReason() == debug_ipc::ThreadRecord::BlockedReason::kException)) { | 
 |       // Make a nicer error message for the common case of requesting stack frames when the thread | 
 |       // is in the wrong state. | 
 |       out->Append( | 
 |           "Stack frames are only available when the thread is either suspended " | 
 |           "or blocked\nin an exception. Use \"pause\" to suspend it."); | 
 |     } else { | 
 |       out->Append("No stack frames.\n"); | 
 |     } | 
 |     out->Complete(); | 
 |     return out; | 
 |   } | 
 |  | 
 |   std::vector<PrettyStackManager::FrameEntry> pretty_stack; | 
 |   if (opts.pretty_stack) { | 
 |     pretty_stack = opts.pretty_stack->ProcessStack(stack); | 
 |   } else { | 
 |     pretty_stack.resize(stack.size()); | 
 |     for (size_t i = 0; i < stack.size(); i++) { | 
 |       pretty_stack[i].begin_index = i; | 
 |       pretty_stack[i].frames.push_back(stack[i]); | 
 |     } | 
 |   } | 
 |  | 
 |   for (const auto& entry : pretty_stack) { | 
 |     // Stack item pretty-printing only happens if there's a pretty match and the current entry | 
 |     // isn't within the range of midden frames. | 
 |     // | 
 |     // One case this doesn't handle is if expanding the range of pretty-stacks means a smaller | 
 |     // matcher might apply that doesn't overlap the user's current frame. To support that, we'd | 
 |     // need to move the logic of no prettifying the current frame to the PrettyStackManager, | 
 |     bool use_pretty = entry.match && !(active_frame_id > entry.begin_index && | 
 |                                        active_frame_id < entry.begin_index + entry.frames.size()); | 
 |  | 
 |     if (use_pretty) { | 
 |       AppendFrameNumber(entry.begin_index, entry.begin_index + entry.frames.size() - 1, | 
 |                         active_frame_id, out.get()); | 
 |       out->Append("«" + entry.match.description + "»"); | 
 |       out->Append(Syntax::kComment, " (-r expands)\n"); | 
 |     } else { | 
 |       for (size_t i = 0; i < entry.frames.size(); i++) { | 
 |         AppendFrameNumber(entry.begin_index + i, entry.begin_index + i, active_frame_id, out.get()); | 
 |  | 
 |         // Supply "-1" for the frame index to suppress printing (we already did it). | 
 |         out->Append(FormatFrame(stack[entry.begin_index + i], opts.frame, -1)); | 
 |         out->Append("\n"); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   out->Complete(); | 
 |   return out; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | fxl::RefPtr<AsyncOutputBuffer> FormatStack(Thread* thread, bool force_update, | 
 |                                            const FormatStackOptions& opts) { | 
 |   auto out = fxl::MakeRefCounted<AsyncOutputBuffer>(); | 
 |   if (!force_update && thread->GetStack().has_all_frames()) { | 
 |     out->Complete(ListCompletedFrames(thread, opts)); | 
 |     return out; | 
 |   } | 
 |  | 
 |   // Request a stack update. | 
 |   thread->GetStack().SyncFrames([thread = thread->GetWeakPtr(), opts, out](const Err& err) { | 
 |     if (!err.has_error() && thread) | 
 |       out->Complete(ListCompletedFrames(thread.get(), opts)); | 
 |     else | 
 |       out->Complete("Thread exited, no frames.\n"); | 
 |   }); | 
 |   return out; | 
 | } | 
 |  | 
 | fxl::RefPtr<AsyncOutputBuffer> FormatFrame(const Frame* frame, const FormatFrameOptions& opts, | 
 |                                            int id) { | 
 |   auto out = fxl::MakeRefCounted<AsyncOutputBuffer>(); | 
 |  | 
 |   if (id >= 0) | 
 |     out->Append(fxl::StringPrintf("Frame %d ", id)); | 
 |  | 
 |   const Location& location = frame->GetLocation(); | 
 |   out->Append(FormatLocation(location, opts.loc)); | 
 |  | 
 |   if (frame->IsInline()) | 
 |     out->Append(Syntax::kComment, " (inline)"); | 
 |  | 
 |   // IP address and stack pointers. | 
 |   if (opts.detail == FormatFrameOptions::kVerbose) { | 
 |     out->Append(Syntax::kComment, fxl::StringPrintf("\n      IP = 0x%" PRIx64 ", SP = 0x%" PRIx64, | 
 |                                                     frame->GetAddress(), frame->GetStackPointer())); | 
 |  | 
 |     // TODO(brettw) make this work when the frame base is asynchronous. | 
 |     if (auto bp = frame->GetBasePointer()) | 
 |       out->Append(Syntax::kComment, fxl::StringPrintf(", base = 0x%" PRIx64, *bp)); | 
 |   } | 
 |  | 
 |   if (opts.detail != FormatFrameOptions::kSimple && location.symbol()) { | 
 |     const Function* func = location.symbol().Get()->AsFunction(); | 
 |     if (func) { | 
 |       // Always list function parameters in the order specified. | 
 |       for (const auto& param : func->parameters()) { | 
 |         const Variable* value = param.Get()->AsVariable(); | 
 |         if (!value) | 
 |           continue;  // Symbols are corrupt. | 
 |  | 
 |         out->Append("\n      ");  // Indent. | 
 |         out->Append(FormatVariableForConsole(value, opts.variable, frame->GetEvalContext())); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   out->Complete(); | 
 |   return out; | 
 | } | 
 |  | 
 | }  // namespace zxdb |