[debugger] Use async fingerprints in finish controller.

The FinishThreadController previously did its own asynchronous search
for stack frames using IP/SP. This is moved to the asynchronous
fingerprint getter on the Stack which removes complexity from the finish
thread controller.

There should be no user-visible change.

TEST=unit

Change-Id: Id11ca7d3d06179bbb3b4ff42e6dac14a646a9336
diff --git a/bin/zxdb/client/finish_thread_controller.cc b/bin/zxdb/client/finish_thread_controller.cc
index 223ecb7..12b5ba4 100644
--- a/bin/zxdb/client/finish_thread_controller.cc
+++ b/bin/zxdb/client/finish_thread_controller.cc
@@ -12,30 +12,36 @@
 
 namespace zxdb {
 
-FinishThreadController::FinishThreadController(const Stack& stack,
-                                               size_t frame_to_finish) {
+FinishThreadController::FinishThreadController(Stack& stack,
+                                               size_t frame_to_finish)
+    : weak_factory_(this) {
   FXL_DCHECK(frame_to_finish < stack.size());
 
-  // TODO(brettw) use new async frame fingerprint computation here.
-  if (frame_to_finish == stack.size() - 1) {
-    if (stack.has_all_frames()) {
-      // Request to finish the oldest stack frame.
-      to_address_ = 0;
-      from_frame_fingerprint_ = *stack.GetFrameFingerprint(frame_to_finish);
-    } else {
-      // Need to sync frames so we can find the calling one. Save the IP and
-      // SP which will be used to re-find the frame in question.
-      // TODO(brettw) this won't handle inline frame selection properly.
-      frame_ip_ = stack[frame_to_finish]->GetAddress();
-      frame_ip_ = stack[frame_to_finish]->GetStackPointer();
-    }
-  } else {
+  if (auto found_fingerprint = stack.GetFrameFingerprint(frame_to_finish)) {
     // Common case where the frame to finish has a previous frame and the
-    // frame fingerprint and return address are known.
-    FXL_DCHECK(stack.size() > frame_to_finish + 1);
+    // frame fingerprint and return address are known. If the frame's
+    // fingerprint can be computed, that means that the previous stack frame is
+    // available (or known not to exist).
     // TODO(brettw) this won't handle inline frame selection properly.
-    to_address_ = stack[frame_to_finish + 1]->GetAddress();
-    from_frame_fingerprint_ = *stack.GetFrameFingerprint(frame_to_finish);
+    from_frame_fingerprint_ = *found_fingerprint;
+    if (frame_to_finish == stack.size() - 1)
+      to_address_ = 0;  // Request to finish oldest stack frame.
+    else
+      to_address_ = stack[frame_to_finish + 1]->GetAddress();
+  } else {
+    // Fingerprint needs an asynchronous request.
+    stack.GetFrameFingerprint(
+        frame_to_finish,
+        [weak_this = weak_factory_.GetWeakPtr()](
+            const Err& err, size_t new_index, FrameFingerprint fingerprint) {
+          if (!weak_this)
+            return;
+
+          weak_this->from_frame_fingerprint_ = fingerprint;
+          if (weak_this->thread())
+            weak_this->InitWithFingerprintAndThread();
+          // Otherwise the callback will be issued when the thread is set.
+        });
   }
 }
 
@@ -57,18 +63,11 @@
     Thread* thread, std::function<void(const Err&)> cb) {
   set_thread(thread);
 
-  if (from_frame_fingerprint_.is_valid()) {
-    // The fingerprint was already computed in the constructor, can skip
-    // directly to setting up the breakpoint.
-    InitWithFingerprint(std::move(cb));
-  } else {
-    // Need to request stack frames to get the frame fingerpring and return
-    // address (both of which require previous frames). We can capture |this|
-    // here since the thread owns this thread controller.
-    thread->GetStack().SyncFrames([ this, cb = std::move(cb) ](const Err& err) {
-      InitWithStack(err, this->thread()->GetStack(), std::move(cb));
-    });
-  }
+  init_callback_ = std::move(cb);
+
+  if (from_frame_fingerprint_)
+    InitWithFingerprintAndThread();
+  // Otherwise the callback will be called when the fingerprint is know.
 }
 
 ThreadController::ContinueOp FinishThreadController::GetContinueOp() {
@@ -77,62 +76,19 @@
   return ContinueOp::Continue();
 }
 
-void FinishThreadController::InitWithStack(const Err& err, const Stack& stack,
-                                           std::function<void(const Err&)> cb) {
-  // Note if this was called asynchronously the thread could be resumed
-  // and it could have no frames, or totally different ones.
-  if (err.has_error()) {
-    cb(err);
-    return;
-  }
+void FinishThreadController::InitWithFingerprintAndThread() {
+  FXL_DCHECK(init_callback_ && from_frame_fingerprint_);
 
-  // Find the frame corresponding to the requested one.
-  constexpr size_t kNotFound = std::numeric_limits<size_t>::max();
-  size_t requested_index = kNotFound;
-  for (size_t i = 0; i < stack.size(); i++) {
-    if (stack[i]->GetAddress() == frame_ip_ &&
-        stack[i]->GetStackPointer() == frame_sp_) {
-      requested_index = i;
-      break;
-    }
-  }
-  if (requested_index == kNotFound) {
-    cb(Err("The stack changed before \"finish\" could start."));
-    return;
-  }
-
-  if (requested_index == stack.size() - 1) {
-    // "Finish" from the bottom-most stack frame just continues the
-    // program to completion.
-    cb(Err());
-    return;
-  }
-
-  // The stack frame to exit to is just the next one up.
-  to_address_ = stack[requested_index + 1]->GetAddress();
-  if (!to_address_) {
-    // Often the bottom-most stack frame will have a 0 IP which obviously
-    // we can't return to. Treat this the same as when returning from the
-    // last frame and just continue.
-    cb(Err());
-    return;
-  }
-  from_frame_fingerprint_ =
-      *thread()->GetStack().GetFrameFingerprint(requested_index);
-  InitWithFingerprint(std::move(cb));
-}
-
-void FinishThreadController::InitWithFingerprint(
-    std::function<void(const Err&)> cb) {
   if (to_address_ == 0) {
     // There is no return address. This will happen when trying to finish the
     // oldest stack frame. Nothing to do.
-    cb(Err());
+    init_callback_(Err());
+    init_callback_ = nullptr;
     return;
   }
 
   until_controller_ = std::make_unique<UntilThreadController>(
-      InputLocation(to_address_), from_frame_fingerprint_,
+      InputLocation(to_address_), *from_frame_fingerprint_,
       UntilThreadController::kRunUntilOlderFrame);
 
   // Give the "until" controller a dummy callback and execute the callback
@@ -145,7 +101,9 @@
   // We can run faster without waiting for the round-trip, and the IPC will
   // serialize so the breakpoint set happens before the thread resume.
   until_controller_->InitWithThread(thread(), [](const Err&) {});
-  cb(Err());
+
+  init_callback_(Err());
+  init_callback_ = nullptr;
 }
 
 }  // namespace zxdb
diff --git a/bin/zxdb/client/finish_thread_controller.h b/bin/zxdb/client/finish_thread_controller.h
index 3cfbb9f..e702b12 100644
--- a/bin/zxdb/client/finish_thread_controller.h
+++ b/bin/zxdb/client/finish_thread_controller.h
@@ -4,10 +4,12 @@
 
 #pragma once
 
+#include <optional>
 #include <vector>
 
 #include "garnet/bin/zxdb/client/frame_fingerprint.h"
 #include "garnet/bin/zxdb/client/thread_controller.h"
+#include "lib/fxl/memory/weak_ptr.h"
 
 namespace zxdb {
 
@@ -22,7 +24,7 @@
  public:
   // Finishes the given frame of the stack, leaving control at frame
   // |frame_to_finish + 1] when the controller is complete.
-  FinishThreadController(const Stack& stack, size_t frame_to_finish);
+  FinishThreadController(Stack& stack, size_t frame_to_finish);
 
   ~FinishThreadController() override;
 
@@ -36,37 +38,34 @@
   const char* GetName() const override { return "Finish"; }
 
  private:
-  // Callback for when the thread has loaded its stack frames. This will
-  // compute the to_frame_fingerprint_.
-  void InitWithStack(const Err& err, const Stack& stack, std::function<void(const Err&)> cb);
-
-  bool HaveAddressAndFingerprint() const;
-
-  // Does final initialization given the to_frame_fingerprint_ is known or we
-  // know we don't need it.
-  void InitWithFingerprint(std::function<void(const Err&)> cb);
-
-  // The instruction and stack pointer of the frame when the address and
-  // fingerprint are not known. The SP allows disambiguation for two frames
-  // at the same address. These will be nonzero only when we need to
-  // asynchronously request data to compute the address or fingerprint.
-  uint64_t frame_ip_ = 0;
-  uint64_t frame_sp_ = 0;
+  // Called when both the frame fingerprint and the thread are known. Does
+  // final initialization.
+  void InitWithFingerprintAndThread();
 
   // The from_frame_fingerprint_ will indicate the frame we're trying to
   // finish. This being valid indicates that the stack has been fetched and
-  // the frame in question located.
+  // the frame in question located (the operation may be async).
+  //
+  // This uses std::optional so the fingerprint can be tested to see if it's
+  // been set, even if the stack returns the null fingerprint (might happen
+  // for the bottom of the stack).
   //
   // The to_address_ will be the address we're returning to. This will be
   // computed at the same time as from_frame_fingerprint_, but may be 0 if
   // the user is trying to finish the oldest stack frame (there's no address
   // to return to).
   uint64_t to_address_ = 0;
-  FrameFingerprint from_frame_fingerprint_;
+  std::optional<FrameFingerprint> from_frame_fingerprint_;
+
+  // The fingerprint can be computed asynchronously with initialization. This
+  // holds the InitWithThread callback for InitWithFingerprintAndThread().
+  std::function<void(const Err&)> init_callback_;
 
   // Will be non-null when stepping out. During initialization or when stepping
   // out of the earliest stack frame, this can be null.
   std::unique_ptr<UntilThreadController> until_controller_;
+
+  fxl::WeakPtrFactory<FinishThreadController> weak_factory_;
 };
 
 }  // namespace zxdb
diff --git a/bin/zxdb/client/step_over_thread_controller.cc b/bin/zxdb/client/step_over_thread_controller.cc
index 6eab82f..33ce364 100644
--- a/bin/zxdb/client/step_over_thread_controller.cc
+++ b/bin/zxdb/client/step_over_thread_controller.cc
@@ -92,7 +92,7 @@
     return kStop;
   }
 
-  const auto& stack = thread()->GetStack();
+  Stack& stack = thread()->GetStack();
   if (stack.size() < 2) {
     Log("In a newer frame but there are not enough frames to step out.");
     return kStop;
diff --git a/bin/zxdb/client/step_thread_controller.cc b/bin/zxdb/client/step_thread_controller.cc
index a068652..5d8a862 100644
--- a/bin/zxdb/client/step_thread_controller.cc
+++ b/bin/zxdb/client/step_thread_controller.cc
@@ -120,7 +120,7 @@
   // function that we call from here.
   FXL_DCHECK(!finish_unsymolized_function_);
 
-  const auto& stack = thread()->GetStack();
+  Stack& stack = thread()->GetStack();
   if (stack.empty())
     return kStop;  // Agent sent bad state, give up trying to step.
 
diff --git a/bin/zxdb/console/verbs_thread.cc b/bin/zxdb/console/verbs_thread.cc
index c6caea2..bfc24ac 100644
--- a/bin/zxdb/console/verbs_thread.cc
+++ b/bin/zxdb/console/verbs_thread.cc
@@ -277,7 +277,7 @@
   if (err.has_error())
     return err;
 
-  const Stack& stack = cmd.thread()->GetStack();
+  Stack& stack = cmd.thread()->GetStack();
   size_t frame_index;
   if (auto found_frame_index = stack.IndexForFrame(cmd.frame()))
     frame_index = *found_frame_index;