brintos

brintos / llvm-project-archived public Read only

0
0
Text · 23.8 KiB · bdb6bb5 Raw
630 lines · cpp
1//===-- EventHelper.h -----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "EventHelper.h"10#include "Breakpoint.h"11#include "BreakpointBase.h"12#include "DAP.h"13#include "DAPError.h"14#include "DAPLog.h"15#include "DAPSessionManager.h"16#include "Handler/ResponseHandler.h"17#include "JSONUtils.h"18#include "LLDBUtils.h"19#include "Protocol/ProtocolEvents.h"20#include "Protocol/ProtocolRequests.h"21#include "Protocol/ProtocolTypes.h"22#include "ProtocolUtils.h"23#include "lldb/API/SBEvent.h"24#include "lldb/API/SBFileSpec.h"25#include "lldb/API/SBListener.h"26#include "lldb/API/SBPlatform.h"27#include "lldb/API/SBStream.h"28#include "llvm/Support/Error.h"29#include "llvm/Support/FormatVariadic.h"30#include "llvm/Support/Threading.h"31#include <mutex>32#include <utility>33 34#if defined(_WIN32)35#define NOMINMAX36#include <windows.h>37 38#ifndef PATH_MAX39#define PATH_MAX MAX_PATH40#endif41#endif42 43using namespace llvm;44 45namespace lldb_dap {46 47static void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) {48  llvm::json::Object event(CreateEventObject("thread"));49  llvm::json::Object body;50  body.try_emplace("reason", "exited");51  body.try_emplace("threadId", (int64_t)tid);52  event.try_emplace("body", std::move(body));53  dap.SendJSON(llvm::json::Value(std::move(event)));54}55 56/// Get capabilities based on the configured target.57static llvm::DenseSet<AdapterFeature> GetTargetBasedCapabilities(DAP &dap) {58  llvm::DenseSet<AdapterFeature> capabilities;59  if (!dap.target.IsValid())60    return capabilities;61 62  const llvm::StringRef target_triple = dap.target.GetTriple();63  if (target_triple.starts_with("x86"))64    capabilities.insert(protocol::eAdapterFeatureStepInTargetsRequest);65 66  // We only support restarting launch requests not attach requests.67  if (dap.last_launch_request)68    capabilities.insert(protocol::eAdapterFeatureRestartRequest);69 70  return capabilities;71}72 73void SendExtraCapabilities(DAP &dap) {74  protocol::Capabilities capabilities = dap.GetCustomCapabilities();75  llvm::DenseSet<AdapterFeature> target_capabilities =76      GetTargetBasedCapabilities(dap);77 78  capabilities.supportedFeatures.insert(target_capabilities.begin(),79                                        target_capabilities.end());80 81  protocol::CapabilitiesEventBody body;82  body.capabilities = std::move(capabilities);83 84  // Only notify the client if supportedFeatures changed.85  if (!body.capabilities.supportedFeatures.empty())86    dap.Send(protocol::Event{"capabilities", std::move(body)});87}88 89// "ProcessEvent": {90//   "allOf": [91//     { "$ref": "#/definitions/Event" },92//     {93//       "type": "object",94//       "description": "The event indicates that the debugger has begun95//       debugging a new process. Either one that it has launched, or one that96//       it has attached to.", "properties": {97//         "event": {98//           "type": "string",99//           "enum": [ "process" ]100//         },101//         "body": {102//           "type": "object",103//           "properties": {104//             "name": {105//               "type": "string",106//               "description": "The logical name of the process. This is107//               usually the full path to process's executable file. Example:108//               /home/example/myproj/program.js."109//             },110//             "systemProcessId": {111//               "type": "integer",112//               "description": "The process ID of the debugged process, as113//               assigned by the operating system. This property should be114//               omitted for logical processes that do not map to operating115//               system processes on the machine."116//             },117//             "isLocalProcess": {118//               "type": "boolean",119//               "description": "If true, the process is running on the same120//               computer as the debug adapter."121//             },122//             "startMethod": {123//               "type": "string",124//               "enum": [ "launch", "attach", "attachForSuspendedLaunch" ],125//               "description": "Describes how the debug engine started126//               debugging this process.", "enumDescriptions": [127//                 "Process was launched under the debugger.",128//                 "Debugger attached to an existing process.",129//                 "A project launcher component has launched a new process in a130//                 suspended state and then asked the debugger to attach."131//               ]132//             },133//             "pointerSize": {134//               "type": "integer",135//               "description": "The size of a pointer or address for this136//               process, in bits. This value may be used by clients when137//               formatting addresses for display."138//             }139//           },140//           "required": [ "name" ]141//         }142//       },143//       "required": [ "event", "body" ]144//     }145//   ]146// },147void SendProcessEvent(DAP &dap, LaunchMethod launch_method) {148  lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();149  char exe_path[PATH_MAX];150  exe_fspec.GetPath(exe_path, sizeof(exe_path));151  llvm::json::Object event(CreateEventObject("process"));152  llvm::json::Object body;153  EmplaceSafeString(body, "name", exe_path);154  const auto pid = dap.target.GetProcess().GetProcessID();155  body.try_emplace("systemProcessId", (int64_t)pid);156  body.try_emplace("isLocalProcess", dap.target.GetPlatform().IsHost());157  body.try_emplace("pointerSize", dap.target.GetAddressByteSize() * 8);158  const char *startMethod = nullptr;159  switch (launch_method) {160  case Launch:161    startMethod = "launch";162    break;163  case Attach:164    startMethod = "attach";165    break;166  case AttachForSuspendedLaunch:167    startMethod = "attachForSuspendedLaunch";168    break;169  }170  body.try_emplace("startMethod", startMethod);171  event.try_emplace("body", std::move(body));172  dap.SendJSON(llvm::json::Value(std::move(event)));173}174 175// Send a thread stopped event for all threads as long as the process176// is stopped.177llvm::Error SendThreadStoppedEvent(DAP &dap, bool on_entry) {178  lldb::SBMutex lock = dap.GetAPIMutex();179  std::lock_guard<lldb::SBMutex> guard(lock);180 181  lldb::SBProcess process = dap.target.GetProcess();182  if (!process.IsValid())183    return make_error<DAPError>("invalid process");184 185  lldb::StateType state = process.GetState();186  if (!lldb::SBDebugger::StateIsStoppedState(state))187    return make_error<NotStoppedError>();188 189  llvm::DenseSet<lldb::tid_t> old_thread_ids;190  old_thread_ids.swap(dap.thread_ids);191  uint32_t stop_id = on_entry ? 0 : process.GetStopID();192  const uint32_t num_threads = process.GetNumThreads();193 194  // First make a pass through the threads to see if the focused thread195  // has a stop reason. In case the focus thread doesn't have a stop196  // reason, remember the first thread that has a stop reason so we can197  // set it as the focus thread if below if needed.198  lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;199  uint32_t num_threads_with_reason = 0;200  bool focus_thread_exists = false;201  for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {202    lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);203    const lldb::tid_t tid = thread.GetThreadID();204    const bool has_reason = ThreadHasStopReason(thread);205    // If the focus thread doesn't have a stop reason, clear the thread ID206    if (tid == dap.focus_tid) {207      focus_thread_exists = true;208      if (!has_reason)209        dap.focus_tid = LLDB_INVALID_THREAD_ID;210    }211    if (has_reason) {212      ++num_threads_with_reason;213      if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)214        first_tid_with_reason = tid;215    }216  }217 218  // We will have cleared dap.focus_tid if the focus thread doesn't have219  // a stop reason, so if it was cleared, or wasn't set, or doesn't exist,220  // then set the focus thread to the first thread with a stop reason.221  if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID)222    dap.focus_tid = first_tid_with_reason;223 224  // If no threads stopped with a reason, then report the first one so225  // we at least let the UI know we stopped.226  if (num_threads_with_reason == 0) {227    lldb::SBThread thread = process.GetThreadAtIndex(0);228    dap.focus_tid = thread.GetThreadID();229    dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));230  } else {231    for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {232      lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);233      dap.thread_ids.insert(thread.GetThreadID());234      if (ThreadHasStopReason(thread)) {235        dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));236      }237    }238  }239 240  for (const auto &tid : old_thread_ids) {241    auto end = dap.thread_ids.end();242    auto pos = dap.thread_ids.find(tid);243    if (pos == end)244      SendThreadExitedEvent(dap, tid);245  }246 247  dap.RunStopCommands();248  return Error::success();249}250 251// Send a "terminated" event to indicate the process is done being252// debugged.253void SendTerminatedEvent(DAP &dap) { dap.SendTerminatedEvent(); }254 255// Grab any STDOUT and STDERR from the process and send it up to VS Code256// via an "output" event to the "stdout" and "stderr" categories.257void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process) {258  char buffer[OutputBufferSize];259  size_t count;260  while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)261    dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));262  while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0)263    dap.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));264}265 266// Send a "continued" event to indicate the process is in the running state.267void SendContinuedEvent(DAP &dap) {268  lldb::SBProcess process = dap.target.GetProcess();269  if (!process.IsValid()) {270    return;271  }272 273  // If the focus thread is not set then we haven't reported any thread status274  // to the client, so nothing to report.275  if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) {276    return;277  }278 279  llvm::json::Object event(CreateEventObject("continued"));280  llvm::json::Object body;281  body.try_emplace("threadId", (int64_t)dap.focus_tid);282  body.try_emplace("allThreadsContinued", true);283  event.try_emplace("body", std::move(body));284  dap.SendJSON(llvm::json::Value(std::move(event)));285}286 287// Send a "exited" event to indicate the process has exited.288void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) {289  llvm::json::Object event(CreateEventObject("exited"));290  llvm::json::Object body;291  body.try_emplace("exitCode", (int64_t)process.GetExitStatus());292  event.try_emplace("body", std::move(body));293  dap.SendJSON(llvm::json::Value(std::move(event)));294}295 296void SendInvalidatedEvent(297    DAP &dap, llvm::ArrayRef<protocol::InvalidatedEventBody::Area> areas,298    lldb::tid_t tid) {299  if (!dap.clientFeatures.contains(protocol::eClientFeatureInvalidatedEvent))300    return;301  protocol::InvalidatedEventBody body;302  body.areas = areas;303 304  if (tid != LLDB_INVALID_THREAD_ID)305    body.threadId = tid;306 307  dap.Send(protocol::Event{"invalidated", std::move(body)});308}309 310void SendMemoryEvent(DAP &dap, lldb::SBValue variable) {311  if (!dap.clientFeatures.contains(protocol::eClientFeatureMemoryEvent))312    return;313  protocol::MemoryEventBody body;314  body.memoryReference = variable.GetLoadAddress();315  body.count = variable.GetByteSize();316  if (body.memoryReference == LLDB_INVALID_ADDRESS)317    return;318  dap.Send(protocol::Event{"memory", std::move(body)});319}320 321// Event handler functions that are called by EventThread.322// These handlers extract the necessary objects from events and find the323// appropriate DAP instance to handle them, maintaining compatibility with324// the original DAP::Handle*Event pattern while supporting multi-session325// debugging.326 327void HandleProcessEvent(const lldb::SBEvent &event, bool &process_exited,328                        Log *log) {329  lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);330 331  // Find the DAP instance that owns this process's target.332  DAP *dap = DAPSessionManager::FindDAP(process.GetTarget());333  if (!dap) {334    DAP_LOG(log, "Unable to find DAP instance for process {0}",335            process.GetProcessID());336    return;337  }338 339  const uint32_t event_mask = event.GetType();340 341  if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {342    auto state = lldb::SBProcess::GetStateFromEvent(event);343    switch (state) {344    case lldb::eStateConnected:345    case lldb::eStateDetached:346    case lldb::eStateInvalid:347    case lldb::eStateUnloaded:348      break;349    case lldb::eStateAttaching:350    case lldb::eStateCrashed:351    case lldb::eStateLaunching:352    case lldb::eStateStopped:353    case lldb::eStateSuspended:354      // Only report a stopped event if the process was not355      // automatically restarted.356      if (!lldb::SBProcess::GetRestartedFromEvent(event)) {357        SendStdOutStdErr(*dap, process);358        if (llvm::Error err = SendThreadStoppedEvent(*dap))359          DAP_LOG_ERROR(dap->log, std::move(err),360                        "({1}) reporting thread stopped: {0}",361                        dap->GetClientName());362      }363      break;364    case lldb::eStateRunning:365    case lldb::eStateStepping:366      dap->WillContinue();367      SendContinuedEvent(*dap);368      break;369    case lldb::eStateExited:370      lldb::SBStream stream;371      process.GetStatus(stream);372      dap->SendOutput(OutputType::Console, stream.GetData());373 374      // When restarting, we can get an "exited" event for the process we375      // just killed with the old PID, or even with no PID. In that case376      // we don't have to terminate the session.377      if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID ||378          process.GetProcessID() == dap->restarting_process_id) {379        dap->restarting_process_id = LLDB_INVALID_PROCESS_ID;380      } else {381        // Run any exit LLDB commands the user specified in the382        // launch.json383        dap->RunExitCommands();384        SendProcessExitedEvent(*dap, process);385        dap->SendTerminatedEvent();386        process_exited = true;387      }388      break;389    }390  } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||391             (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {392    SendStdOutStdErr(*dap, process);393  }394}395 396void HandleTargetEvent(const lldb::SBEvent &event, Log *log) {397  lldb::SBTarget target = lldb::SBTarget::GetTargetFromEvent(event);398 399  // Find the DAP instance that owns this target.400  DAP *dap = DAPSessionManager::FindDAP(target);401  if (!dap) {402    DAP_LOG(log, "Unable to find DAP instance for target");403    return;404  }405 406  const uint32_t event_mask = event.GetType();407  if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded ||408      event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded ||409      event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded ||410      event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) {411    const uint32_t num_modules = lldb::SBTarget::GetNumModulesFromEvent(event);412    const bool remove_module =413        event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded;414 415    // NOTE: Both mutexes must be acquired to prevent deadlock when416    // handling `modules_request`, which also requires both locks.417    lldb::SBMutex api_mutex = dap->GetAPIMutex();418    const std::scoped_lock<lldb::SBMutex, std::mutex> guard(api_mutex,419                                                            dap->modules_mutex);420    for (uint32_t i = 0; i < num_modules; ++i) {421      lldb::SBModule module =422          lldb::SBTarget::GetModuleAtIndexFromEvent(i, event);423 424      std::optional<protocol::Module> p_module =425          CreateModule(dap->target, module, remove_module);426      if (!p_module)427        continue;428 429      llvm::StringRef module_id = p_module->id;430 431      const bool module_exists = dap->modules.contains(module_id);432      if (remove_module && module_exists) {433        dap->modules.erase(module_id);434        dap->Send(protocol::Event{435            "module", protocol::ModuleEventBody{436                          std::move(p_module).value(),437                          protocol::ModuleEventBody::eReasonRemoved}});438      } else if (module_exists) {439        dap->Send(protocol::Event{440            "module", protocol::ModuleEventBody{441                          std::move(p_module).value(),442                          protocol::ModuleEventBody::eReasonChanged}});443      } else if (!remove_module) {444        dap->modules.insert(module_id);445        dap->Send(protocol::Event{446            "module",447            protocol::ModuleEventBody{std::move(p_module).value(),448                                      protocol::ModuleEventBody::eReasonNew}});449      }450    }451  } else if (event_mask & lldb::SBTarget::eBroadcastBitNewTargetCreated) {452    // For NewTargetCreated events, GetTargetFromEvent returns the parent453    // target, and GetCreatedTargetFromEvent returns the newly created target.454    lldb::SBTarget created_target =455        lldb::SBTarget::GetCreatedTargetFromEvent(event);456 457    if (!target.IsValid() || !created_target.IsValid()) {458      DAP_LOG(log, "Received NewTargetCreated event but parent or "459                   "created target is invalid");460      return;461    }462 463    // Send a startDebugging reverse request with the debugger and target464    // IDs. The new DAP instance will use these IDs to find the existing465    // debugger and target via FindDebuggerWithID and466    // FindTargetByGloballyUniqueID.467    llvm::json::Object configuration;468    configuration.try_emplace("type", "lldb");469    configuration.try_emplace("debuggerId",470                              created_target.GetDebugger().GetID());471    configuration.try_emplace("targetId", created_target.GetGloballyUniqueID());472    configuration.try_emplace("name", created_target.GetTargetSessionName());473 474    llvm::json::Object request;475    request.try_emplace("request", "attach");476    request.try_emplace("configuration", std::move(configuration));477 478    dap->SendReverseRequest<LogFailureResponseHandler>("startDebugging",479                                                       std::move(request));480  }481}482 483void HandleBreakpointEvent(const lldb::SBEvent &event, Log *log) {484  const uint32_t event_mask = event.GetType();485  if (!(event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged))486    return;487 488  lldb::SBBreakpoint bp = lldb::SBBreakpoint::GetBreakpointFromEvent(event);489  if (!bp.IsValid())490    return;491 492  // Find the DAP instance that owns this breakpoint's target.493  DAP *dap = DAPSessionManager::FindDAP(bp.GetTarget());494  if (!dap) {495    DAP_LOG(log, "Unable to find DAP instance for breakpoint");496    return;497  }498 499  auto event_type = lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);500  auto breakpoint = Breakpoint(*dap, bp);501  // If the breakpoint was set through DAP, it will have the502  // BreakpointBase::kDAPBreakpointLabel. Regardless of whether503  // locations were added, removed, or resolved, the breakpoint isn't504  // going away and the reason is always "changed".505  if ((event_type & lldb::eBreakpointEventTypeLocationsAdded ||506       event_type & lldb::eBreakpointEventTypeLocationsRemoved ||507       event_type & lldb::eBreakpointEventTypeLocationsResolved) &&508      breakpoint.MatchesName(BreakpointBase::kDAPBreakpointLabel)) {509    // As the DAP client already knows the path of this breakpoint, we510    // don't need to send it back as part of the "changed" event. This511    // avoids sending paths that should be source mapped. Note that512    // CreateBreakpoint doesn't apply source mapping and certain513    // implementation ignore the source part of this event anyway.514    protocol::Breakpoint protocol_bp = breakpoint.ToProtocolBreakpoint();515 516    // "source" is not needed here, unless we add adapter data to be517    // saved by the client.518    if (protocol_bp.source && !protocol_bp.source->adapterData)519      protocol_bp.source = std::nullopt;520 521    llvm::json::Object body;522    body.try_emplace("breakpoint", protocol_bp);523    body.try_emplace("reason", "changed");524 525    llvm::json::Object bp_event = CreateEventObject("breakpoint");526    bp_event.try_emplace("body", std::move(body));527 528    dap->SendJSON(llvm::json::Value(std::move(bp_event)));529  }530}531 532void HandleThreadEvent(const lldb::SBEvent &event, Log *log) {533  uint32_t event_type = event.GetType();534 535  if (!(event_type & lldb::SBThread::eBroadcastBitStackChanged))536    return;537 538  lldb::SBThread thread = lldb::SBThread::GetThreadFromEvent(event);539  if (!thread.IsValid())540    return;541 542  // Find the DAP instance that owns this thread's process/target.543  DAP *dap = DAPSessionManager::FindDAP(thread.GetProcess().GetTarget());544  if (!dap) {545    DAP_LOG(log, "Unable to find DAP instance for thread");546    return;547  }548 549  SendInvalidatedEvent(*dap, {protocol::InvalidatedEventBody::eAreaStacks},550                       thread.GetThreadID());551}552 553void HandleDiagnosticEvent(const lldb::SBEvent &event, Log *log) {554  // Global debugger events - send to all DAP instances.555  std::vector<DAP *> active_instances =556      DAPSessionManager::GetInstance().GetActiveSessions();557  for (DAP *dap_instance : active_instances) {558    if (!dap_instance)559      continue;560 561    lldb::SBStructuredData data =562        lldb::SBDebugger::GetDiagnosticFromEvent(event);563    if (!data.IsValid())564      continue;565 566    std::string type = GetStringValue(data.GetValueForKey("type"));567    std::string message = GetStringValue(data.GetValueForKey("message"));568    dap_instance->SendOutput(OutputType::Important,569                             llvm::formatv("{0}: {1}", type, message).str());570  }571}572 573// Note: EventThread() is architecturally different from the other functions in574// this file. While the functions above are event helpers that operate on a575// single DAP instance (taking `DAP &dap` as a parameter), EventThread() is a576// shared event processing loop that:577// 1. Listens to events from a shared debugger instance578// 2. Dispatches events to the appropriate handler, which internally finds the579//    DAP instance using DAPSessionManager::FindDAP()580// 3. Handles events for multiple different DAP sessions581// This allows multiple DAP sessions to share a single debugger and event582// thread, which is essential for the target handoff mechanism where child583// processes/targets are debugged in separate DAP sessions.584//585// All events from the debugger, target, process, thread and frames are586// received in this function that runs in its own thread. We are using a587// "FILE *" to output packets back to VS Code and they have mutexes in them588// them prevent multiple threads from writing simultaneously so no locking589// is required.590void EventThread(lldb::SBDebugger debugger, lldb::SBBroadcaster broadcaster,591                 llvm::StringRef client_name, Log *log) {592  llvm::set_thread_name("lldb.DAP.client." + client_name + ".event_handler");593  lldb::SBListener listener = debugger.GetListener();594  broadcaster.AddListener(listener, eBroadcastBitStopEventThread);595  debugger.GetBroadcaster().AddListener(596      listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning);597 598  // listen for thread events.599  listener.StartListeningForEventClass(600      debugger, lldb::SBThread::GetBroadcasterClassName(),601      lldb::SBThread::eBroadcastBitStackChanged);602 603  lldb::SBEvent event;604  bool done = false;605  while (!done) {606    if (!listener.WaitForEvent(UINT32_MAX, event))607      continue;608 609    const uint32_t event_mask = event.GetType();610    if (lldb::SBProcess::EventIsProcessEvent(event)) {611      HandleProcessEvent(event, /*&process_exited=*/done, log);612    } else if (lldb::SBTarget::EventIsTargetEvent(event)) {613      HandleTargetEvent(event, log);614    } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {615      HandleBreakpointEvent(event, log);616    } else if (lldb::SBThread::EventIsThreadEvent(event)) {617      HandleThreadEvent(event, log);618    } else if (event_mask & lldb::eBroadcastBitError ||619               event_mask & lldb::eBroadcastBitWarning) {620      HandleDiagnosticEvent(event, log);621    } else if (event.BroadcasterMatchesRef(broadcaster)) {622      if (event_mask & eBroadcastBitStopEventThread) {623        done = true;624      }625    }626  }627}628 629} // namespace lldb_dap630