
This adds new types for setExceptionBreakpoints and adds support for `supportsExceptionFilterOptions`, which allows exception breakpoints to set a condition. While testing this, I noticed that obj-c exception catch breakpoints may not be working correctly in lldb-dap.
176 lines
5.8 KiB
C++
176 lines
5.8 KiB
C++
//===-- ProtocolUtils.cpp -------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ProtocolUtils.h"
|
|
#include "LLDBUtils.h"
|
|
|
|
#include "lldb/API/SBDebugger.h"
|
|
#include "lldb/API/SBFormat.h"
|
|
#include "lldb/API/SBMutex.h"
|
|
#include "lldb/API/SBStream.h"
|
|
#include "lldb/API/SBTarget.h"
|
|
#include "lldb/API/SBThread.h"
|
|
#include "lldb/Host/PosixApi.h" // Adds PATH_MAX for windows
|
|
#include <optional>
|
|
|
|
using namespace lldb_dap::protocol;
|
|
namespace lldb_dap {
|
|
|
|
static bool ShouldDisplayAssemblySource(
|
|
lldb::SBAddress address,
|
|
lldb::StopDisassemblyType stop_disassembly_display) {
|
|
if (stop_disassembly_display == lldb::eStopDisassemblyTypeNever)
|
|
return false;
|
|
|
|
if (stop_disassembly_display == lldb::eStopDisassemblyTypeAlways)
|
|
return true;
|
|
|
|
// A line entry of 0 indicates the line is compiler generated i.e. no source
|
|
// file is associated with the frame.
|
|
auto line_entry = address.GetLineEntry();
|
|
auto file_spec = line_entry.GetFileSpec();
|
|
if (!file_spec.IsValid() || line_entry.GetLine() == 0 ||
|
|
line_entry.GetLine() == LLDB_INVALID_LINE_NUMBER)
|
|
return true;
|
|
|
|
if (stop_disassembly_display == lldb::eStopDisassemblyTypeNoSource &&
|
|
!file_spec.Exists()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static protocol::Source CreateAssemblySource(const lldb::SBTarget &target,
|
|
lldb::SBAddress address) {
|
|
protocol::Source source;
|
|
|
|
auto symbol = address.GetSymbol();
|
|
std::string name;
|
|
if (symbol.IsValid()) {
|
|
source.sourceReference = symbol.GetStartAddress().GetLoadAddress(target);
|
|
name = symbol.GetName();
|
|
} else {
|
|
const auto load_addr = address.GetLoadAddress(target);
|
|
source.sourceReference = load_addr;
|
|
name = GetLoadAddressString(load_addr);
|
|
}
|
|
|
|
lldb::SBModule module = address.GetModule();
|
|
if (module.IsValid()) {
|
|
lldb::SBFileSpec file_spec = module.GetFileSpec();
|
|
if (file_spec.IsValid()) {
|
|
std::string path = GetSBFileSpecPath(file_spec);
|
|
if (!path.empty())
|
|
source.path = path + '`' + name;
|
|
}
|
|
}
|
|
|
|
source.name = std::move(name);
|
|
|
|
// Mark the source as deemphasized since users will only be able to view
|
|
// assembly for these frames.
|
|
source.presentationHint =
|
|
protocol::Source::PresentationHint::eSourcePresentationHintDeemphasize;
|
|
|
|
return source;
|
|
}
|
|
|
|
protocol::Source CreateSource(const lldb::SBFileSpec &file) {
|
|
protocol::Source source;
|
|
if (file.IsValid()) {
|
|
if (const char *name = file.GetFilename())
|
|
source.name = name;
|
|
char path[PATH_MAX] = "";
|
|
if (file.GetPath(path, sizeof(path)) &&
|
|
lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX))
|
|
source.path = path;
|
|
}
|
|
return source;
|
|
}
|
|
|
|
protocol::Source CreateSource(lldb::SBAddress address, lldb::SBTarget &target) {
|
|
lldb::SBDebugger debugger = target.GetDebugger();
|
|
lldb::StopDisassemblyType stop_disassembly_display =
|
|
GetStopDisassemblyDisplay(debugger);
|
|
if (ShouldDisplayAssemblySource(address, stop_disassembly_display))
|
|
return CreateAssemblySource(target, address);
|
|
|
|
lldb::SBLineEntry line_entry = GetLineEntryForAddress(target, address);
|
|
return CreateSource(line_entry.GetFileSpec());
|
|
}
|
|
|
|
bool IsAssemblySource(const protocol::Source &source) {
|
|
// According to the specification, a source must have either `path` or
|
|
// `sourceReference` specified. We use `path` for sources with known source
|
|
// code, and `sourceReferences` when falling back to assembly.
|
|
return source.sourceReference.value_or(0) != 0;
|
|
}
|
|
|
|
std::string GetLoadAddressString(const lldb::addr_t addr) {
|
|
return "0x" + llvm::utohexstr(addr, false, 16);
|
|
}
|
|
|
|
protocol::Thread CreateThread(lldb::SBThread &thread, lldb::SBFormat &format) {
|
|
std::string name;
|
|
lldb::SBStream stream;
|
|
if (format && thread.GetDescriptionWithFormat(format, stream).Success()) {
|
|
name = stream.GetData();
|
|
} else {
|
|
llvm::StringRef thread_name(thread.GetName());
|
|
llvm::StringRef queue_name(thread.GetQueueName());
|
|
|
|
if (!thread_name.empty()) {
|
|
name = thread_name.str();
|
|
} else if (!queue_name.empty()) {
|
|
auto kind = thread.GetQueue().GetKind();
|
|
std::string queue_kind_label = "";
|
|
if (kind == lldb::eQueueKindSerial)
|
|
queue_kind_label = " (serial)";
|
|
else if (kind == lldb::eQueueKindConcurrent)
|
|
queue_kind_label = " (concurrent)";
|
|
|
|
name = llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
|
|
queue_name, queue_kind_label)
|
|
.str();
|
|
} else {
|
|
name = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
|
|
}
|
|
}
|
|
return protocol::Thread{thread.GetThreadID(), name};
|
|
}
|
|
|
|
std::vector<protocol::Thread> GetThreads(lldb::SBProcess process,
|
|
lldb::SBFormat &format) {
|
|
lldb::SBMutex lock = process.GetTarget().GetAPIMutex();
|
|
std::lock_guard<lldb::SBMutex> guard(lock);
|
|
|
|
std::vector<protocol::Thread> threads;
|
|
|
|
const uint32_t num_threads = process.GetNumThreads();
|
|
threads.reserve(num_threads);
|
|
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
|
|
lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
|
|
threads.emplace_back(CreateThread(thread, format));
|
|
}
|
|
return threads;
|
|
}
|
|
|
|
protocol::ExceptionBreakpointsFilter
|
|
CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
|
|
protocol::ExceptionBreakpointsFilter filter;
|
|
filter.filter = bp.GetFilter();
|
|
filter.label = bp.GetLabel();
|
|
filter.description = bp.GetLabel();
|
|
filter.defaultState = ExceptionBreakpoint::kDefaultValue;
|
|
filter.supportsCondition = true;
|
|
return filter;
|
|
}
|
|
|
|
} // namespace lldb_dap
|