diff options
Diffstat (limited to 'lldb/source/Target')
28 files changed, 1830 insertions, 578 deletions
diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp index c3342caf8742..6e8772cbd142 100644 --- a/lldb/source/Target/ABI.cpp +++ b/lldb/source/Target/ABI.cpp @@ -16,7 +16,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Log.h" -#include "llvm/Support/TargetRegistry.h" +#include "llvm/MC/TargetRegistry.h" #include <cctype> using namespace lldb; @@ -214,33 +214,39 @@ std::unique_ptr<llvm::MCRegisterInfo> ABI::MakeMCRegisterInfo(const ArchSpec &ar return info_up; } -void RegInfoBasedABI::AugmentRegisterInfo(RegisterInfo &info) { - if (info.kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM && - info.kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) - return; +void RegInfoBasedABI::AugmentRegisterInfo( + std::vector<DynamicRegisterInfo::Register> ®s) { + for (DynamicRegisterInfo::Register &info : regs) { + if (info.regnum_ehframe != LLDB_INVALID_REGNUM && + info.regnum_dwarf != LLDB_INVALID_REGNUM) + continue; - RegisterInfo abi_info; - if (!GetRegisterInfoByName(info.name, abi_info)) - return; + RegisterInfo abi_info; + if (!GetRegisterInfoByName(info.name.GetStringRef(), abi_info)) + continue; - if (info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM) - info.kinds[eRegisterKindEHFrame] = abi_info.kinds[eRegisterKindEHFrame]; - if (info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM) - info.kinds[eRegisterKindDWARF] = abi_info.kinds[eRegisterKindDWARF]; - if (info.kinds[eRegisterKindGeneric] == LLDB_INVALID_REGNUM) - info.kinds[eRegisterKindGeneric] = abi_info.kinds[eRegisterKindGeneric]; + if (info.regnum_ehframe == LLDB_INVALID_REGNUM) + info.regnum_ehframe = abi_info.kinds[eRegisterKindEHFrame]; + if (info.regnum_dwarf == LLDB_INVALID_REGNUM) + info.regnum_dwarf = abi_info.kinds[eRegisterKindDWARF]; + if (info.regnum_generic == LLDB_INVALID_REGNUM) + info.regnum_generic = abi_info.kinds[eRegisterKindGeneric]; + } } -void MCBasedABI::AugmentRegisterInfo(RegisterInfo &info) { - uint32_t eh, dwarf; - std::tie(eh, dwarf) = GetEHAndDWARFNums(info.name); +void MCBasedABI::AugmentRegisterInfo( + std::vector<DynamicRegisterInfo::Register> ®s) { + for (DynamicRegisterInfo::Register &info : regs) { + uint32_t eh, dwarf; + std::tie(eh, dwarf) = GetEHAndDWARFNums(info.name.GetStringRef()); - if (info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM) - info.kinds[eRegisterKindEHFrame] = eh; - if (info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM) - info.kinds[eRegisterKindDWARF] = dwarf; - if (info.kinds[eRegisterKindGeneric] == LLDB_INVALID_REGNUM) - info.kinds[eRegisterKindGeneric] = GetGenericNum(info.name); + if (info.regnum_ehframe == LLDB_INVALID_REGNUM) + info.regnum_ehframe = eh; + if (info.regnum_dwarf == LLDB_INVALID_REGNUM) + info.regnum_dwarf = dwarf; + if (info.regnum_generic == LLDB_INVALID_REGNUM) + info.regnum_generic = GetGenericNum(info.name.GetStringRef()); + } } std::pair<uint32_t, uint32_t> diff --git a/lldb/source/Target/DynamicRegisterInfo.cpp b/lldb/source/Target/DynamicRegisterInfo.cpp new file mode 100644 index 000000000000..9f894f86aea8 --- /dev/null +++ b/lldb/source/Target/DynamicRegisterInfo.cpp @@ -0,0 +1,819 @@ +//===-- DynamicRegisterInfo.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 "lldb/Target/DynamicRegisterInfo.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/StringExtractor.h" +#include "lldb/Utility/StructuredData.h" + +using namespace lldb; +using namespace lldb_private; + +DynamicRegisterInfo::DynamicRegisterInfo( + const lldb_private::StructuredData::Dictionary &dict, + const lldb_private::ArchSpec &arch) { + SetRegisterInfo(dict, arch); +} + +DynamicRegisterInfo::DynamicRegisterInfo(DynamicRegisterInfo &&info) { + MoveFrom(std::move(info)); +} + +DynamicRegisterInfo & +DynamicRegisterInfo::operator=(DynamicRegisterInfo &&info) { + MoveFrom(std::move(info)); + return *this; +} + +void DynamicRegisterInfo::MoveFrom(DynamicRegisterInfo &&info) { + m_regs = std::move(info.m_regs); + m_sets = std::move(info.m_sets); + m_set_reg_nums = std::move(info.m_set_reg_nums); + m_set_names = std::move(info.m_set_names); + m_value_regs_map = std::move(info.m_value_regs_map); + m_invalidate_regs_map = std::move(info.m_invalidate_regs_map); + + m_reg_data_byte_size = info.m_reg_data_byte_size; + m_finalized = info.m_finalized; + + if (m_finalized) { + const size_t num_sets = m_sets.size(); + for (size_t set = 0; set < num_sets; ++set) + m_sets[set].registers = m_set_reg_nums[set].data(); + } + + info.Clear(); +} + +llvm::Expected<uint32_t> DynamicRegisterInfo::ByteOffsetFromSlice( + uint32_t index, llvm::StringRef slice_str, lldb::ByteOrder byte_order) { + // Slices use the following format: + // REGNAME[MSBIT:LSBIT] + // REGNAME - name of the register to grab a slice of + // MSBIT - the most significant bit at which the current register value + // starts at + // LSBIT - the least significant bit at which the current register value + // ends at + static llvm::Regex g_bitfield_regex( + "([A-Za-z_][A-Za-z0-9_]*)\\[([0-9]+):([0-9]+)\\]"); + llvm::SmallVector<llvm::StringRef, 4> matches; + if (!g_bitfield_regex.match(slice_str, &matches)) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "failed to match against register bitfield regex (slice: %s)", + slice_str.str().c_str()); + + llvm::StringRef reg_name_str = matches[1]; + llvm::StringRef msbit_str = matches[2]; + llvm::StringRef lsbit_str = matches[3]; + uint32_t msbit; + uint32_t lsbit; + if (!llvm::to_integer(msbit_str, msbit) || + !llvm::to_integer(lsbit_str, lsbit)) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), "msbit (%s) or lsbit (%s) are invalid", + msbit_str.str().c_str(), lsbit_str.str().c_str()); + + if (msbit <= lsbit) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "msbit (%u) must be greater than lsbit (%u)", + msbit, lsbit); + + const uint32_t msbyte = msbit / 8; + const uint32_t lsbyte = lsbit / 8; + + const RegisterInfo *containing_reg_info = GetRegisterInfo(reg_name_str); + if (!containing_reg_info) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "invalid concrete register \"%s\"", + reg_name_str.str().c_str()); + + const uint32_t max_bit = containing_reg_info->byte_size * 8; + + if (msbit > max_bit) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "msbit (%u) must be less than the bitsize of the register \"%s\" (%u)", + msbit, reg_name_str.str().c_str(), max_bit); + if (lsbit > max_bit) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "lsbit (%u) must be less than the bitsize of the register \"%s\" (%u)", + lsbit, reg_name_str.str().c_str(), max_bit); + + m_invalidate_regs_map[containing_reg_info->kinds[eRegisterKindLLDB]] + .push_back(index); + m_value_regs_map[index].push_back( + containing_reg_info->kinds[eRegisterKindLLDB]); + m_invalidate_regs_map[index].push_back( + containing_reg_info->kinds[eRegisterKindLLDB]); + + if (byte_order == eByteOrderLittle) + return containing_reg_info->byte_offset + lsbyte; + if (byte_order == eByteOrderBig) + return containing_reg_info->byte_offset + msbyte; + llvm_unreachable("Invalid byte order"); +} + +llvm::Expected<uint32_t> DynamicRegisterInfo::ByteOffsetFromComposite( + uint32_t index, StructuredData::Array &composite_reg_list, + lldb::ByteOrder byte_order) { + const size_t num_composite_regs = composite_reg_list.GetSize(); + if (num_composite_regs == 0) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "\"composite\" list is empty"); + + uint32_t composite_offset = UINT32_MAX; + for (uint32_t composite_idx = 0; composite_idx < num_composite_regs; + ++composite_idx) { + ConstString composite_reg_name; + if (!composite_reg_list.GetItemAtIndexAsString(composite_idx, + composite_reg_name, nullptr)) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "\"composite\" list value is not a Python string at index %d", + composite_idx); + + const RegisterInfo *composite_reg_info = + GetRegisterInfo(composite_reg_name.GetStringRef()); + if (!composite_reg_info) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "failed to find composite register by name: \"%s\"", + composite_reg_name.GetCString()); + + composite_offset = + std::min(composite_offset, composite_reg_info->byte_offset); + m_value_regs_map[index].push_back( + composite_reg_info->kinds[eRegisterKindLLDB]); + m_invalidate_regs_map[composite_reg_info->kinds[eRegisterKindLLDB]] + .push_back(index); + m_invalidate_regs_map[index].push_back( + composite_reg_info->kinds[eRegisterKindLLDB]); + } + + return composite_offset; +} + +llvm::Expected<uint32_t> DynamicRegisterInfo::ByteOffsetFromRegInfoDict( + uint32_t index, StructuredData::Dictionary ®_info_dict, + lldb::ByteOrder byte_order) { + uint32_t byte_offset; + if (reg_info_dict.GetValueForKeyAsInteger("offset", byte_offset)) + return byte_offset; + + // No offset for this register, see if the register has a value + // expression which indicates this register is part of another register. + // Value expressions are things like "rax[31:0]" which state that the + // current register's value is in a concrete register "rax" in bits 31:0. + // If there is a value expression we can calculate the offset + llvm::StringRef slice_str; + if (reg_info_dict.GetValueForKeyAsString("slice", slice_str, nullptr)) + return ByteOffsetFromSlice(index, slice_str, byte_order); + + StructuredData::Array *composite_reg_list; + if (reg_info_dict.GetValueForKeyAsArray("composite", composite_reg_list)) + return ByteOffsetFromComposite(index, *composite_reg_list, byte_order); + + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "insufficient data to calculate byte offset"); +} + +size_t +DynamicRegisterInfo::SetRegisterInfo(const StructuredData::Dictionary &dict, + const ArchSpec &arch) { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT); + assert(!m_finalized); + StructuredData::Array *sets = nullptr; + if (dict.GetValueForKeyAsArray("sets", sets)) { + const uint32_t num_sets = sets->GetSize(); + for (uint32_t i = 0; i < num_sets; ++i) { + ConstString set_name; + if (sets->GetItemAtIndexAsString(i, set_name) && !set_name.IsEmpty()) { + m_sets.push_back({set_name.AsCString(), nullptr, 0, nullptr}); + } else { + Clear(); + printf("error: register sets must have valid names\n"); + return 0; + } + } + m_set_reg_nums.resize(m_sets.size()); + } + + StructuredData::Array *regs = nullptr; + if (!dict.GetValueForKeyAsArray("registers", regs)) + return 0; + + const ByteOrder byte_order = arch.GetByteOrder(); + + const uint32_t num_regs = regs->GetSize(); + // typedef std::map<std::string, std::vector<std::string> > + // InvalidateNameMap; + // InvalidateNameMap invalidate_map; + for (uint32_t i = 0; i < num_regs; ++i) { + StructuredData::Dictionary *reg_info_dict = nullptr; + if (!regs->GetItemAtIndexAsDictionary(i, reg_info_dict)) { + Clear(); + printf("error: items in the 'registers' array must be dictionaries\n"); + regs->DumpToStdout(); + return 0; + } + + // { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, + // 'encoding':'uint' , 'format':'hex' , 'set': 0, 'ehframe' : 2, + // 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', }, + RegisterInfo reg_info; + std::vector<uint32_t> value_regs; + std::vector<uint32_t> invalidate_regs; + memset(®_info, 0, sizeof(reg_info)); + + ConstString name_val; + ConstString alt_name_val; + if (!reg_info_dict->GetValueForKeyAsString("name", name_val, nullptr)) { + Clear(); + printf("error: registers must have valid names and offsets\n"); + reg_info_dict->DumpToStdout(); + return 0; + } + reg_info.name = name_val.GetCString(); + reg_info_dict->GetValueForKeyAsString("alt-name", alt_name_val, nullptr); + reg_info.alt_name = alt_name_val.GetCString(); + + llvm::Expected<uint32_t> byte_offset = + ByteOffsetFromRegInfoDict(i, *reg_info_dict, byte_order); + if (byte_offset) + reg_info.byte_offset = byte_offset.get(); + else { + LLDB_LOG_ERROR(log, byte_offset.takeError(), + "error while parsing register {1}: {0}", reg_info.name); + Clear(); + reg_info_dict->DumpToStdout(); + return 0; + } + + int64_t bitsize = 0; + if (!reg_info_dict->GetValueForKeyAsInteger("bitsize", bitsize)) { + Clear(); + printf("error: invalid or missing 'bitsize' key/value pair in register " + "dictionary\n"); + reg_info_dict->DumpToStdout(); + return 0; + } + + reg_info.byte_size = bitsize / 8; + + llvm::StringRef format_str; + if (reg_info_dict->GetValueForKeyAsString("format", format_str, nullptr)) { + if (OptionArgParser::ToFormat(format_str.str().c_str(), reg_info.format, + nullptr) + .Fail()) { + Clear(); + printf("error: invalid 'format' value in register dictionary\n"); + reg_info_dict->DumpToStdout(); + return 0; + } + } else { + reg_info_dict->GetValueForKeyAsInteger("format", reg_info.format, + eFormatHex); + } + + llvm::StringRef encoding_str; + if (reg_info_dict->GetValueForKeyAsString("encoding", encoding_str)) + reg_info.encoding = Args::StringToEncoding(encoding_str, eEncodingUint); + else + reg_info_dict->GetValueForKeyAsInteger("encoding", reg_info.encoding, + eEncodingUint); + + size_t set = 0; + if (!reg_info_dict->GetValueForKeyAsInteger<size_t>("set", set, -1) || + set >= m_sets.size()) { + Clear(); + printf("error: invalid 'set' value in register dictionary, valid values " + "are 0 - %i\n", + (int)set); + reg_info_dict->DumpToStdout(); + return 0; + } + + // Fill in the register numbers + reg_info.kinds[lldb::eRegisterKindLLDB] = i; + reg_info.kinds[lldb::eRegisterKindProcessPlugin] = i; + uint32_t eh_frame_regno = LLDB_INVALID_REGNUM; + reg_info_dict->GetValueForKeyAsInteger("gcc", eh_frame_regno, + LLDB_INVALID_REGNUM); + if (eh_frame_regno == LLDB_INVALID_REGNUM) + reg_info_dict->GetValueForKeyAsInteger("ehframe", eh_frame_regno, + LLDB_INVALID_REGNUM); + reg_info.kinds[lldb::eRegisterKindEHFrame] = eh_frame_regno; + reg_info_dict->GetValueForKeyAsInteger( + "dwarf", reg_info.kinds[lldb::eRegisterKindDWARF], LLDB_INVALID_REGNUM); + llvm::StringRef generic_str; + if (reg_info_dict->GetValueForKeyAsString("generic", generic_str)) + reg_info.kinds[lldb::eRegisterKindGeneric] = + Args::StringToGenericRegister(generic_str); + else + reg_info_dict->GetValueForKeyAsInteger( + "generic", reg_info.kinds[lldb::eRegisterKindGeneric], + LLDB_INVALID_REGNUM); + + // Check if this register invalidates any other register values when it is + // modified + StructuredData::Array *invalidate_reg_list = nullptr; + if (reg_info_dict->GetValueForKeyAsArray("invalidate-regs", + invalidate_reg_list)) { + const size_t num_regs = invalidate_reg_list->GetSize(); + if (num_regs > 0) { + for (uint32_t idx = 0; idx < num_regs; ++idx) { + ConstString invalidate_reg_name; + uint64_t invalidate_reg_num; + if (invalidate_reg_list->GetItemAtIndexAsString( + idx, invalidate_reg_name)) { + const RegisterInfo *invalidate_reg_info = + GetRegisterInfo(invalidate_reg_name.GetStringRef()); + if (invalidate_reg_info) { + m_invalidate_regs_map[i].push_back( + invalidate_reg_info->kinds[eRegisterKindLLDB]); + } else { + // TODO: print error invalid slice string that doesn't follow the + // format + printf("error: failed to find a 'invalidate-regs' register for " + "\"%s\" while parsing register \"%s\"\n", + invalidate_reg_name.GetCString(), reg_info.name); + } + } else if (invalidate_reg_list->GetItemAtIndexAsInteger( + idx, invalidate_reg_num)) { + if (invalidate_reg_num != UINT64_MAX) + m_invalidate_regs_map[i].push_back(invalidate_reg_num); + else + printf("error: 'invalidate-regs' list value wasn't a valid " + "integer\n"); + } else { + printf("error: 'invalidate-regs' list value wasn't a python string " + "or integer\n"); + } + } + } else { + printf("error: 'invalidate-regs' contained an empty list\n"); + } + } + + // Calculate the register offset + const size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; + if (m_reg_data_byte_size < end_reg_offset) + m_reg_data_byte_size = end_reg_offset; + + m_regs.push_back(reg_info); + m_set_reg_nums[set].push_back(i); + } + Finalize(arch); + return m_regs.size(); +} + +size_t DynamicRegisterInfo::SetRegisterInfo( + std::vector<DynamicRegisterInfo::Register> &®s, + const ArchSpec &arch) { + assert(!m_finalized); + + for (auto it : llvm::enumerate(regs)) { + uint32_t local_regnum = it.index(); + const DynamicRegisterInfo::Register ® = it.value(); + + assert(reg.name); + assert(reg.set_name); + + if (!reg.value_regs.empty()) + m_value_regs_map[local_regnum] = std::move(reg.value_regs); + if (!reg.invalidate_regs.empty()) + m_invalidate_regs_map[local_regnum] = std::move(reg.invalidate_regs); + if (reg.value_reg_offset != 0) { + assert(reg.value_regs.size() == 1); + m_value_reg_offset_map[local_regnum] = reg.value_reg_offset; + } + + struct RegisterInfo reg_info { + reg.name.AsCString(), reg.alt_name.AsCString(), reg.byte_size, + reg.byte_offset, reg.encoding, reg.format, + {reg.regnum_ehframe, reg.regnum_dwarf, reg.regnum_generic, + reg.regnum_remote, local_regnum}, + // value_regs and invalidate_regs are filled by Finalize() + nullptr, nullptr + }; + + m_regs.push_back(reg_info); + + uint32_t set = GetRegisterSetIndexByName(reg.set_name, true); + assert(set < m_sets.size()); + assert(set < m_set_reg_nums.size()); + assert(set < m_set_names.size()); + m_set_reg_nums[set].push_back(local_regnum); + }; + + Finalize(arch); + return m_regs.size(); +} + +void DynamicRegisterInfo::Finalize(const ArchSpec &arch) { + if (m_finalized) + return; + + m_finalized = true; + const size_t num_sets = m_sets.size(); + for (size_t set = 0; set < num_sets; ++set) { + assert(m_sets.size() == m_set_reg_nums.size()); + m_sets[set].num_registers = m_set_reg_nums[set].size(); + m_sets[set].registers = m_set_reg_nums[set].data(); + } + + // make sure value_regs are terminated with LLDB_INVALID_REGNUM + + for (reg_to_regs_map::iterator pos = m_value_regs_map.begin(), + end = m_value_regs_map.end(); + pos != end; ++pos) { + if (pos->second.back() != LLDB_INVALID_REGNUM) + pos->second.push_back(LLDB_INVALID_REGNUM); + } + + // Now update all value_regs with each register info as needed + const size_t num_regs = m_regs.size(); + for (size_t i = 0; i < num_regs; ++i) { + if (m_value_regs_map.find(i) != m_value_regs_map.end()) + m_regs[i].value_regs = m_value_regs_map[i].data(); + else + m_regs[i].value_regs = nullptr; + } + + // Expand all invalidation dependencies + for (reg_to_regs_map::iterator pos = m_invalidate_regs_map.begin(), + end = m_invalidate_regs_map.end(); + pos != end; ++pos) { + const uint32_t reg_num = pos->first; + + if (m_regs[reg_num].value_regs) { + reg_num_collection extra_invalid_regs; + for (const uint32_t invalidate_reg_num : pos->second) { + reg_to_regs_map::iterator invalidate_pos = + m_invalidate_regs_map.find(invalidate_reg_num); + if (invalidate_pos != m_invalidate_regs_map.end()) { + for (const uint32_t concrete_invalidate_reg_num : + invalidate_pos->second) { + if (concrete_invalidate_reg_num != reg_num) + extra_invalid_regs.push_back(concrete_invalidate_reg_num); + } + } + } + pos->second.insert(pos->second.end(), extra_invalid_regs.begin(), + extra_invalid_regs.end()); + } + } + + // sort and unique all invalidate registers and make sure each is terminated + // with LLDB_INVALID_REGNUM + for (reg_to_regs_map::iterator pos = m_invalidate_regs_map.begin(), + end = m_invalidate_regs_map.end(); + pos != end; ++pos) { + if (pos->second.size() > 1) { + llvm::sort(pos->second.begin(), pos->second.end()); + reg_num_collection::iterator unique_end = + std::unique(pos->second.begin(), pos->second.end()); + if (unique_end != pos->second.end()) + pos->second.erase(unique_end, pos->second.end()); + } + assert(!pos->second.empty()); + if (pos->second.back() != LLDB_INVALID_REGNUM) + pos->second.push_back(LLDB_INVALID_REGNUM); + } + + // Now update all invalidate_regs with each register info as needed + for (size_t i = 0; i < num_regs; ++i) { + if (m_invalidate_regs_map.find(i) != m_invalidate_regs_map.end()) + m_regs[i].invalidate_regs = m_invalidate_regs_map[i].data(); + else + m_regs[i].invalidate_regs = nullptr; + } + + // Check if we need to automatically set the generic registers in case they + // weren't set + bool generic_regs_specified = false; + for (const auto ® : m_regs) { + if (reg.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) { + generic_regs_specified = true; + break; + } + } + + if (!generic_regs_specified) { + switch (arch.GetMachine()) { + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + case llvm::Triple::aarch64_be: + for (auto ® : m_regs) { + if (strcmp(reg.name, "pc") == 0) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if ((strcmp(reg.name, "fp") == 0) || + (strcmp(reg.name, "x29") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if ((strcmp(reg.name, "lr") == 0) || + (strcmp(reg.name, "x30") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; + else if ((strcmp(reg.name, "sp") == 0) || + (strcmp(reg.name, "x31") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if (strcmp(reg.name, "cpsr") == 0) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + break; + + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + for (auto ® : m_regs) { + if ((strcmp(reg.name, "pc") == 0) || (strcmp(reg.name, "r15") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if ((strcmp(reg.name, "sp") == 0) || + (strcmp(reg.name, "r13") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if ((strcmp(reg.name, "lr") == 0) || + (strcmp(reg.name, "r14") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; + else if ((strcmp(reg.name, "r7") == 0) && + arch.GetTriple().getVendor() == llvm::Triple::Apple) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if ((strcmp(reg.name, "r11") == 0) && + arch.GetTriple().getVendor() != llvm::Triple::Apple) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if (strcmp(reg.name, "fp") == 0) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if (strcmp(reg.name, "cpsr") == 0) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + break; + + case llvm::Triple::x86: + for (auto ® : m_regs) { + if ((strcmp(reg.name, "eip") == 0) || (strcmp(reg.name, "pc") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if ((strcmp(reg.name, "esp") == 0) || + (strcmp(reg.name, "sp") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if ((strcmp(reg.name, "ebp") == 0) || + (strcmp(reg.name, "fp") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if ((strcmp(reg.name, "eflags") == 0) || + (strcmp(reg.name, "flags") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + break; + + case llvm::Triple::x86_64: + for (auto ® : m_regs) { + if ((strcmp(reg.name, "rip") == 0) || (strcmp(reg.name, "pc") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if ((strcmp(reg.name, "rsp") == 0) || + (strcmp(reg.name, "sp") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if ((strcmp(reg.name, "rbp") == 0) || + (strcmp(reg.name, "fp") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if ((strcmp(reg.name, "rflags") == 0) || + (strcmp(reg.name, "eflags") == 0) || + (strcmp(reg.name, "flags") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + break; + + default: + break; + } + } + + // At this stage call ConfigureOffsets to calculate register offsets for + // targets supporting dynamic offset calculation. It also calculates + // total byte size of register data. + ConfigureOffsets(); + + // Check if register info is reconfigurable + // AArch64 SVE register set has configurable register sizes + if (arch.GetTriple().isAArch64()) { + for (const auto ® : m_regs) { + if (strcmp(reg.name, "vg") == 0) { + m_is_reconfigurable = true; + break; + } + } + } +} + +void DynamicRegisterInfo::ConfigureOffsets() { + // We are going to create a map between remote (eRegisterKindProcessPlugin) + // and local (eRegisterKindLLDB) register numbers. This map will give us + // remote register numbers in increasing order for offset calculation. + std::map<uint32_t, uint32_t> remote_to_local_regnum_map; + for (const auto ® : m_regs) + remote_to_local_regnum_map[reg.kinds[eRegisterKindProcessPlugin]] = + reg.kinds[eRegisterKindLLDB]; + + // At this stage we manually calculate g/G packet offsets of all primary + // registers, only if target XML or qRegisterInfo packet did not send + // an offset explicitly. + uint32_t reg_offset = 0; + for (auto const ®num_pair : remote_to_local_regnum_map) { + if (m_regs[regnum_pair.second].byte_offset == LLDB_INVALID_INDEX32 && + m_regs[regnum_pair.second].value_regs == nullptr) { + m_regs[regnum_pair.second].byte_offset = reg_offset; + + reg_offset = m_regs[regnum_pair.second].byte_offset + + m_regs[regnum_pair.second].byte_size; + } + } + + // Now update all value_regs with each register info as needed + for (auto ® : m_regs) { + if (reg.value_regs != nullptr) { + // Assign a valid offset to all pseudo registers that have only a single + // parent register in value_regs list, if not assigned by stub. Pseudo + // registers with value_regs list populated will share same offset as + // that of their corresponding parent register. + if (reg.byte_offset == LLDB_INVALID_INDEX32) { + uint32_t value_regnum = reg.value_regs[0]; + if (value_regnum != LLDB_INVALID_INDEX32 && + reg.value_regs[1] == LLDB_INVALID_INDEX32) { + reg.byte_offset = + GetRegisterInfoAtIndex(value_regnum)->byte_offset; + auto it = m_value_reg_offset_map.find(reg.kinds[eRegisterKindLLDB]); + if (it != m_value_reg_offset_map.end()) + reg.byte_offset += it->second; + } + } + } + + reg_offset = reg.byte_offset + reg.byte_size; + if (m_reg_data_byte_size < reg_offset) + m_reg_data_byte_size = reg_offset; + } +} + +bool DynamicRegisterInfo::IsReconfigurable() { return m_is_reconfigurable; } + +size_t DynamicRegisterInfo::GetNumRegisters() const { return m_regs.size(); } + +size_t DynamicRegisterInfo::GetNumRegisterSets() const { return m_sets.size(); } + +size_t DynamicRegisterInfo::GetRegisterDataByteSize() const { + return m_reg_data_byte_size; +} + +const RegisterInfo * +DynamicRegisterInfo::GetRegisterInfoAtIndex(uint32_t i) const { + if (i < m_regs.size()) + return &m_regs[i]; + return nullptr; +} + +const RegisterInfo *DynamicRegisterInfo::GetRegisterInfo(uint32_t kind, + uint32_t num) const { + uint32_t reg_index = ConvertRegisterKindToRegisterNumber(kind, num); + if (reg_index != LLDB_INVALID_REGNUM) + return &m_regs[reg_index]; + return nullptr; +} + +const RegisterSet *DynamicRegisterInfo::GetRegisterSet(uint32_t i) const { + if (i < m_sets.size()) + return &m_sets[i]; + return nullptr; +} + +uint32_t +DynamicRegisterInfo::GetRegisterSetIndexByName(const ConstString &set_name, + bool can_create) { + name_collection::iterator pos, end = m_set_names.end(); + for (pos = m_set_names.begin(); pos != end; ++pos) { + if (*pos == set_name) + return std::distance(m_set_names.begin(), pos); + } + + m_set_names.push_back(set_name); + m_set_reg_nums.resize(m_set_reg_nums.size() + 1); + RegisterSet new_set = {set_name.AsCString(), nullptr, 0, nullptr}; + m_sets.push_back(new_set); + return m_sets.size() - 1; +} + +uint32_t +DynamicRegisterInfo::ConvertRegisterKindToRegisterNumber(uint32_t kind, + uint32_t num) const { + reg_collection::const_iterator pos, end = m_regs.end(); + for (pos = m_regs.begin(); pos != end; ++pos) { + if (pos->kinds[kind] == num) + return std::distance(m_regs.begin(), pos); + } + + return LLDB_INVALID_REGNUM; +} + +void DynamicRegisterInfo::Clear() { + m_regs.clear(); + m_sets.clear(); + m_set_reg_nums.clear(); + m_set_names.clear(); + m_value_regs_map.clear(); + m_invalidate_regs_map.clear(); + m_reg_data_byte_size = 0; + m_finalized = false; +} + +void DynamicRegisterInfo::Dump() const { + StreamFile s(stdout, false); + const size_t num_regs = m_regs.size(); + s.Printf("%p: DynamicRegisterInfo contains %" PRIu64 " registers:\n", + static_cast<const void *>(this), static_cast<uint64_t>(num_regs)); + for (size_t i = 0; i < num_regs; ++i) { + s.Printf("[%3" PRIu64 "] name = %-10s", (uint64_t)i, m_regs[i].name); + s.Printf(", size = %2u, offset = %4u, encoding = %u, format = %-10s", + m_regs[i].byte_size, m_regs[i].byte_offset, m_regs[i].encoding, + FormatManager::GetFormatAsCString(m_regs[i].format)); + if (m_regs[i].kinds[eRegisterKindProcessPlugin] != LLDB_INVALID_REGNUM) + s.Printf(", process plugin = %3u", + m_regs[i].kinds[eRegisterKindProcessPlugin]); + if (m_regs[i].kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) + s.Printf(", dwarf = %3u", m_regs[i].kinds[eRegisterKindDWARF]); + if (m_regs[i].kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) + s.Printf(", ehframe = %3u", m_regs[i].kinds[eRegisterKindEHFrame]); + if (m_regs[i].kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) + s.Printf(", generic = %3u", m_regs[i].kinds[eRegisterKindGeneric]); + if (m_regs[i].alt_name) + s.Printf(", alt-name = %s", m_regs[i].alt_name); + if (m_regs[i].value_regs) { + s.Printf(", value_regs = [ "); + for (size_t j = 0; m_regs[i].value_regs[j] != LLDB_INVALID_REGNUM; ++j) { + s.Printf("%s ", m_regs[m_regs[i].value_regs[j]].name); + } + s.Printf("]"); + } + if (m_regs[i].invalidate_regs) { + s.Printf(", invalidate_regs = [ "); + for (size_t j = 0; m_regs[i].invalidate_regs[j] != LLDB_INVALID_REGNUM; + ++j) { + s.Printf("%s ", m_regs[m_regs[i].invalidate_regs[j]].name); + } + s.Printf("]"); + } + s.EOL(); + } + + const size_t num_sets = m_sets.size(); + s.Printf("%p: DynamicRegisterInfo contains %" PRIu64 " register sets:\n", + static_cast<const void *>(this), static_cast<uint64_t>(num_sets)); + for (size_t i = 0; i < num_sets; ++i) { + s.Printf("set[%" PRIu64 "] name = %s, regs = [", (uint64_t)i, + m_sets[i].name); + for (size_t idx = 0; idx < m_sets[i].num_registers; ++idx) { + s.Printf("%s ", m_regs[m_sets[i].registers[idx]].name); + } + s.Printf("]\n"); + } +} + +const lldb_private::RegisterInfo * +DynamicRegisterInfo::GetRegisterInfo(llvm::StringRef reg_name) const { + for (auto ®_info : m_regs) + if (reg_info.name == reg_name) + return ®_info; + return nullptr; +} + +void lldb_private::addSupplementaryRegister( + std::vector<DynamicRegisterInfo::Register> ®s, + DynamicRegisterInfo::Register new_reg_info) { + assert(!new_reg_info.value_regs.empty()); + const uint32_t reg_num = regs.size(); + regs.push_back(new_reg_info); + + std::map<uint32_t, std::vector<uint32_t>> new_invalidates; + for (uint32_t value_reg : new_reg_info.value_regs) { + // copy value_regs to invalidate_regs + new_invalidates[reg_num].push_back(value_reg); + + // copy invalidate_regs from the parent register + llvm::append_range(new_invalidates[reg_num], + regs[value_reg].invalidate_regs); + + // add reverse invalidate entries + for (uint32_t x : new_invalidates[reg_num]) + new_invalidates[x].push_back(reg_num); + } + + for (const auto &x : new_invalidates) + llvm::append_range(regs[x.first].invalidate_regs, x.second); +} diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index 7b35da5028ac..eee1ff1512d9 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -108,10 +108,21 @@ void Language::ForEach(std::function<bool(Language *)> callback) { } }); - std::lock_guard<std::mutex> guard(GetLanguagesMutex()); - LanguagesMap &map(GetLanguagesMap()); - for (const auto &entry : map) { - if (!callback(entry.second.get())) + // callback may call a method in Language that attempts to acquire the same + // lock (such as Language::ForEach or Language::FindPlugin). To avoid a + // deadlock, we do not use callback while holding the lock. + std::vector<Language *> loaded_plugins; + { + std::lock_guard<std::mutex> guard(GetLanguagesMutex()); + LanguagesMap &map(GetLanguagesMap()); + for (const auto &entry : map) { + if (entry.second) + loaded_plugins.push_back(entry.second.get()); + } + } + + for (auto *lang : loaded_plugins) { + if (!callback(lang)) break; } } diff --git a/lldb/source/Target/ModuleCache.cpp b/lldb/source/Target/ModuleCache.cpp index dcdc0772b31a..7143fcd2707c 100644 --- a/lldb/source/Target/ModuleCache.cpp +++ b/lldb/source/Target/ModuleCache.cpp @@ -159,7 +159,7 @@ ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); auto file = FileSystem::Instance().Open( - m_file_spec, File::eOpenOptionWrite | File::eOpenOptionCanCreate | + m_file_spec, File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate | File::eOpenOptionCloseOnExec); if (file) m_file_up = std::move(file.get()); diff --git a/lldb/source/Target/OperatingSystem.cpp b/lldb/source/Target/OperatingSystem.cpp index 033a806460da..75762c05151d 100644 --- a/lldb/source/Target/OperatingSystem.cpp +++ b/lldb/source/Target/OperatingSystem.cpp @@ -17,10 +17,9 @@ OperatingSystem *OperatingSystem::FindPlugin(Process *process, const char *plugin_name) { OperatingSystemCreateInstance create_callback = nullptr; if (plugin_name) { - ConstString const_plugin_name(plugin_name); create_callback = PluginManager::GetOperatingSystemCreateCallbackForPluginName( - const_plugin_name); + plugin_name); if (create_callback) { std::unique_ptr<OperatingSystem> instance_up( create_callback(process, true)); diff --git a/lldb/source/Target/PathMappingList.cpp b/lldb/source/Target/PathMappingList.cpp index b660c310ef31..e49f6213cf27 100644 --- a/lldb/source/Target/PathMappingList.cpp +++ b/lldb/source/Target/PathMappingList.cpp @@ -30,11 +30,11 @@ namespace { // with the raw path pair, which doesn't work anymore because the paths have // been normalized when the debug info was loaded. So we need to store // nomalized path pairs to ensure things match up. - ConstString NormalizePath(ConstString path) { - // If we use "path" to construct a FileSpec, it will normalize the path for - // us. We then grab the string and turn it back into a ConstString. - return ConstString(FileSpec(path.GetStringRef()).GetPath()); - } +std::string NormalizePath(llvm::StringRef path) { + // If we use "path" to construct a FileSpec, it will normalize the path for + // us. We then grab the string. + return FileSpec(path).GetPath(); +} } // PathMappingList constructor PathMappingList::PathMappingList() : m_pairs() {} @@ -59,8 +59,8 @@ const PathMappingList &PathMappingList::operator=(const PathMappingList &rhs) { PathMappingList::~PathMappingList() = default; -void PathMappingList::Append(ConstString path, - ConstString replacement, bool notify) { +void PathMappingList::Append(llvm::StringRef path, llvm::StringRef replacement, + bool notify) { ++m_mod_id; m_pairs.emplace_back(pair(NormalizePath(path), NormalizePath(replacement))); if (notify && m_callback) @@ -78,9 +78,8 @@ void PathMappingList::Append(const PathMappingList &rhs, bool notify) { } } -void PathMappingList::Insert(ConstString path, - ConstString replacement, uint32_t index, - bool notify) { +void PathMappingList::Insert(llvm::StringRef path, llvm::StringRef replacement, + uint32_t index, bool notify) { ++m_mod_id; iterator insert_iter; if (index >= m_pairs.size()) @@ -93,9 +92,8 @@ void PathMappingList::Insert(ConstString path, m_callback(*this, m_callback_baton); } -bool PathMappingList::Replace(ConstString path, - ConstString replacement, uint32_t index, - bool notify) { +bool PathMappingList::Replace(llvm::StringRef path, llvm::StringRef replacement, + uint32_t index, bool notify) { if (index >= m_pairs.size()) return false; ++m_mod_id; @@ -218,18 +216,22 @@ bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) co } llvm::Optional<FileSpec> PathMappingList::FindFile(const FileSpec &orig_spec) const { - if (auto remapped = RemapPath(orig_spec.GetPath(), /*only_if_exists=*/true)) + // We must normalize the orig_spec again using the host's path style, + // otherwise there will be mismatch between the host and remote platform + // if they use different path styles. + if (auto remapped = RemapPath(NormalizePath(orig_spec.GetPath()), + /*only_if_exists=*/true)) return remapped; return {}; } -bool PathMappingList::Replace(ConstString path, - ConstString new_path, bool notify) { +bool PathMappingList::Replace(llvm::StringRef path, llvm::StringRef new_path, + bool notify) { uint32_t idx = FindIndexForPath(path); if (idx < m_pairs.size()) { ++m_mod_id; - m_pairs[idx].second = new_path; + m_pairs[idx].second = ConstString(new_path); if (notify && m_callback) m_callback(*this, m_callback_baton); return true; @@ -285,8 +287,8 @@ bool PathMappingList::GetPathsAtIndex(uint32_t idx, ConstString &path, return false; } -uint32_t PathMappingList::FindIndexForPath(ConstString orig_path) const { - const ConstString path = NormalizePath(orig_path); +uint32_t PathMappingList::FindIndexForPath(llvm::StringRef orig_path) const { + const ConstString path = ConstString(NormalizePath(orig_path)); const_iterator pos; const_iterator begin = m_pairs.begin(); const_iterator end = m_pairs.end(); diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index a77ecddfbab6..bd455310f08e 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -155,9 +155,9 @@ void Platform::Terminate() { } } -const PlatformPropertiesSP &Platform::GetGlobalPlatformProperties() { - static const auto g_settings_sp(std::make_shared<PlatformProperties>()); - return g_settings_sp; +PlatformProperties &Platform::GetGlobalPlatformProperties() { + static PlatformProperties g_settings; + return g_settings; } void Platform::SetHostPlatform(const lldb::PlatformSP &platform_sp) { @@ -294,8 +294,8 @@ PlatformSP Platform::Create(ConstString name, Status &error) { if (name == g_host_platform_name) return GetHostPlatform(); - create_callback = - PluginManager::GetPlatformCreateCallbackForPluginName(name); + create_callback = PluginManager::GetPlatformCreateCallbackForPluginName( + name.GetStringRef()); if (create_callback) platform_sp = create_callback(true, nullptr); else @@ -395,18 +395,10 @@ Platform::Platform(bool is_host) LLDB_LOGF(log, "%p Platform::Platform()", static_cast<void *>(this)); } -/// Destructor. -/// -/// The destructor is virtual since this class is designed to be -/// inherited from by the plug-in instance. -Platform::~Platform() { - Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); - LLDB_LOGF(log, "%p Platform::~Platform()", static_cast<void *>(this)); -} +Platform::~Platform() = default; void Platform::GetStatus(Stream &strm) { - std::string s; - strm.Printf(" Platform: %s\n", GetPluginName().GetCString()); + strm.Format(" Platform: {0}\n", GetPluginName()); ArchSpec arch(GetSystemArchitecture()); if (arch.IsValid()) { @@ -421,8 +413,8 @@ void Platform::GetStatus(Stream &strm) { if (!os_version.empty()) { strm.Format("OS Version: {0}", os_version.getAsString()); - if (GetOSBuildString(s)) - strm.Printf(" (%s)", s.c_str()); + if (llvm::Optional<std::string> s = GetOSBuildString()) + strm.Format(" ({0})", *s); strm.EOL(); } @@ -447,8 +439,8 @@ void Platform::GetStatus(Stream &strm) { if (!specific_info.empty()) strm.Printf("Platform-specific connection: %s\n", specific_info.c_str()); - if (GetOSKernelDescription(s)) - strm.Printf(" Kernel: %s\n", s.c_str()); + if (llvm::Optional<std::string> s = GetOSKernelDescription()) + strm.Format(" Kernel: {0}\n", *s); } llvm::VersionTuple Platform::GetOSVersion(Process *process) { @@ -493,28 +485,16 @@ llvm::VersionTuple Platform::GetOSVersion(Process *process) { return llvm::VersionTuple(); } -bool Platform::GetOSBuildString(std::string &s) { - s.clear(); - +llvm::Optional<std::string> Platform::GetOSBuildString() { if (IsHost()) -#if !defined(__linux__) - return HostInfo::GetOSBuildString(s); -#else - return false; -#endif - else - return GetRemoteOSBuildString(s); + return HostInfo::GetOSBuildString(); + return GetRemoteOSBuildString(); } -bool Platform::GetOSKernelDescription(std::string &s) { +llvm::Optional<std::string> Platform::GetOSKernelDescription() { if (IsHost()) -#if !defined(__linux__) - return HostInfo::GetOSKernelDescription(s); -#else - return false; -#endif - else - return GetRemoteOSKernelDescription(s); + return HostInfo::GetOSKernelDescription(); + return GetRemoteOSKernelDescription(); } void Platform::AddClangModuleCompilationOptions( @@ -769,9 +749,8 @@ Status Platform::MakeDirectory(const FileSpec &file_spec, return llvm::sys::fs::create_directory(file_spec.GetPath(), permissions); else { Status error; - error.SetErrorStringWithFormat("remote platform %s doesn't support %s", - GetPluginName().GetCString(), - LLVM_PRETTY_FUNCTION); + error.SetErrorStringWithFormatv("remote platform {0} doesn't support {1}", + GetPluginName(), LLVM_PRETTY_FUNCTION); return error; } } @@ -785,9 +764,8 @@ Status Platform::GetFilePermissions(const FileSpec &file_spec, return Status(Value.getError()); } else { Status error; - error.SetErrorStringWithFormat("remote platform %s doesn't support %s", - GetPluginName().GetCString(), - LLVM_PRETTY_FUNCTION); + error.SetErrorStringWithFormatv("remote platform {0} doesn't support {1}", + GetPluginName(), LLVM_PRETTY_FUNCTION); return error; } } @@ -799,14 +777,13 @@ Status Platform::SetFilePermissions(const FileSpec &file_spec, return llvm::sys::fs::setPermissions(file_spec.GetPath(), Perms); } else { Status error; - error.SetErrorStringWithFormat("remote platform %s doesn't support %s", - GetPluginName().GetCString(), - LLVM_PRETTY_FUNCTION); + error.SetErrorStringWithFormatv("remote platform {0} doesn't support {1}", + GetPluginName(), LLVM_PRETTY_FUNCTION); return error; } } -ConstString Platform::GetName() { return GetPluginName(); } +ConstString Platform::GetName() { return ConstString(GetPluginName()); } const char *Platform::GetHostname() { if (IsHost()) @@ -856,6 +833,7 @@ Platform::ResolveExecutable(const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { Status error; + if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { if (module_spec.GetArchitecture().IsValid()) { error = ModuleList::GetSharedModule(module_spec, exe_module_sp, @@ -866,9 +844,8 @@ Platform::ResolveExecutable(const ModuleSpec &module_spec, // architectures that we should be using (in the correct order) and see // if we can find a match that way ModuleSpec arch_module_spec(module_spec); - for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( - idx, arch_module_spec.GetArchitecture()); - ++idx) { + for (const ArchSpec &arch : GetSupportedArchitectures()) { + arch_module_spec.GetArchitecture() = arch; error = ModuleList::GetSharedModule(arch_module_spec, exe_module_sp, module_search_paths_ptr, nullptr, nullptr); @@ -878,9 +855,74 @@ Platform::ResolveExecutable(const ModuleSpec &module_spec, } } } else { - error.SetErrorStringWithFormat("'%s' does not exist", - module_spec.GetFileSpec().GetPath().c_str()); + error.SetErrorStringWithFormat( + "'%s' does not exist", module_spec.GetFileSpec().GetPath().c_str()); + } + return error; +} + +Status +Platform::ResolveRemoteExecutable(const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) { + Status error; + + // We may connect to a process and use the provided executable (Don't use + // local $PATH). + ModuleSpec resolved_module_spec(module_spec); + + // Resolve any executable within a bundle on MacOSX + Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec()); + + if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()) || + module_spec.GetUUID().IsValid()) { + if (resolved_module_spec.GetArchitecture().IsValid() || + resolved_module_spec.GetUUID().IsValid()) { + error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, + module_search_paths_ptr, nullptr, + nullptr); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact arch wasn't found so + // ask the platform for the architectures that we should be using (in the + // correct order) and see if we can find a match that way + StreamString arch_names; + llvm::ListSeparator LS; + for (const ArchSpec &arch : GetSupportedArchitectures()) { + resolved_module_spec.GetArchitecture() = arch; + error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, + module_search_paths_ptr, nullptr, + nullptr); + // Did we find an executable using one of the + if (error.Success()) { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + arch_names << LS << arch.GetArchitectureName(); + } + + if (error.Fail() || !exe_module_sp) { + if (FileSystem::Instance().Readable(resolved_module_spec.GetFileSpec())) { + error.SetErrorStringWithFormatv( + "'{0}' doesn't contain any '{1}' platform architectures: {2}", + resolved_module_spec.GetFileSpec(), GetPluginName(), + arch_names.GetData()); + } else { + error.SetErrorStringWithFormatv("'{0}' is not readable", + resolved_module_spec.GetFileSpec()); + } + } + } else { + error.SetErrorStringWithFormatv("'{0}' does not exist", + resolved_module_spec.GetFileSpec()); } + return error; } @@ -966,26 +1008,27 @@ ArchSpec Platform::GetAugmentedArchSpec(llvm::StringRef triple) { Status Platform::ConnectRemote(Args &args) { Status error; if (IsHost()) - error.SetErrorStringWithFormat("The currently selected platform (%s) is " - "the host platform and is always connected.", - GetPluginName().GetCString()); + error.SetErrorStringWithFormatv( + "The currently selected platform ({0}) is " + "the host platform and is always connected.", + GetPluginName()); else - error.SetErrorStringWithFormat( - "Platform::ConnectRemote() is not supported by %s", - GetPluginName().GetCString()); + error.SetErrorStringWithFormatv( + "Platform::ConnectRemote() is not supported by {0}", GetPluginName()); return error; } Status Platform::DisconnectRemote() { Status error; if (IsHost()) - error.SetErrorStringWithFormat("The currently selected platform (%s) is " - "the host platform and is always connected.", - GetPluginName().GetCString()); + error.SetErrorStringWithFormatv( + "The currently selected platform ({0}) is " + "the host platform and is always connected.", + GetPluginName()); else - error.SetErrorStringWithFormat( - "Platform::DisconnectRemote() is not supported by %s", - GetPluginName().GetCString()); + error.SetErrorStringWithFormatv( + "Platform::DisconnectRemote() is not supported by {0}", + GetPluginName()); return error; } @@ -1066,36 +1109,19 @@ Status Platform::KillProcess(const lldb::pid_t pid) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); LLDB_LOGF(log, "Platform::%s, pid %" PRIu64, __FUNCTION__, pid); - // Try to find a process plugin to handle this Kill request. If we can't, - // fall back to the default OS implementation. - size_t num_debuggers = Debugger::GetNumDebuggers(); - for (size_t didx = 0; didx < num_debuggers; ++didx) { - DebuggerSP debugger = Debugger::GetDebuggerAtIndex(didx); - lldb_private::TargetList &targets = debugger->GetTargetList(); - for (int tidx = 0; tidx < targets.GetNumTargets(); ++tidx) { - ProcessSP process = targets.GetTargetAtIndex(tidx)->GetProcessSP(); - if (process->GetID() == pid) - return process->Destroy(true); - } - } - if (!IsHost()) { return Status( - "base lldb_private::Platform class can't kill remote processes unless " - "they are controlled by a process plugin"); + "base lldb_private::Platform class can't kill remote processes"); } - Host::Kill(pid, SIGTERM); + Host::Kill(pid, SIGKILL); return Status(); } -lldb::ProcessSP -Platform::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, - Target *target, // Can be nullptr, if nullptr create a - // new target, else use existing one - Status &error) { +lldb::ProcessSP Platform::DebugProcess(ProcessLaunchInfo &launch_info, + Debugger &debugger, Target &target, + Status &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); - LLDB_LOGF(log, "Platform::%s entered (target %p)", __FUNCTION__, - static_cast<void *>(target)); + LLDB_LOG(log, "target = {0})", &target); ProcessSP process_sp; // Make sure we stop at the entry point @@ -1117,7 +1143,7 @@ Platform::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, filter_callback = get_filter_func(++i, iteration_complete)) { if (filter_callback) { // Give this ProcessLaunchInfo filter a chance to adjust the launch info. - error = (*filter_callback)(launch_info, target); + error = (*filter_callback)(launch_info, &target); if (!error.Success()) { LLDB_LOGF(log, "Platform::%s() StructuredDataPlugin launch " @@ -1135,10 +1161,10 @@ Platform::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, __FUNCTION__, launch_info.GetProcessID()); if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { ProcessAttachInfo attach_info(launch_info); - process_sp = Attach(attach_info, debugger, target, error); + process_sp = Attach(attach_info, debugger, &target, error); if (process_sp) { - LLDB_LOGF(log, "Platform::%s Attach() succeeded, Process plugin: %s", - __FUNCTION__, process_sp->GetPluginName().AsCString()); + LLDB_LOG(log, "Attach() succeeded, Process plugin: {0}", + process_sp->GetPluginName()); launch_info.SetHijackListener(attach_info.GetHijackListener()); // Since we attached to the process, it will think it needs to detach @@ -1149,7 +1175,7 @@ Platform::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, // If we didn't have any file actions, the pseudo terminal might have // been used where the secondary side was given as the file to open for - // stdin/out/err after we have already opened the master so we can + // stdin/out/err after we have already opened the primary so we can // read/write stdin/out/err. int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor(); if (pty_fd != PseudoTerminal::invalid_fd) { @@ -1183,6 +1209,35 @@ Platform::GetPlatformForArchitecture(const ArchSpec &arch, return platform_sp; } +std::vector<ArchSpec> +Platform::CreateArchList(llvm::ArrayRef<llvm::Triple::ArchType> archs, + llvm::Triple::OSType os) { + std::vector<ArchSpec> list; + for(auto arch : archs) { + llvm::Triple triple; + triple.setArch(arch); + triple.setOS(os); + list.push_back(ArchSpec(triple)); + } + return list; +} + +bool Platform::GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) { + const auto &archs = GetSupportedArchitectures(); + if (idx >= archs.size()) + return false; + arch = archs[idx]; + return true; +} + +std::vector<ArchSpec> Platform::GetSupportedArchitectures() { + std::vector<ArchSpec> result; + ArchSpec arch; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(idx, arch); ++idx) + result.push_back(arch); + return result; +} + /// Lets a platform answer if it is compatible with a given /// architecture and the target triple contained within. bool Platform::IsCompatibleArchitecture(const ArchSpec &arch, @@ -1191,26 +1246,13 @@ bool Platform::IsCompatibleArchitecture(const ArchSpec &arch, // If the architecture is invalid, we must answer true... if (arch.IsValid()) { ArchSpec platform_arch; - // Try for an exact architecture match first. - if (exact_arch_match) { - for (uint32_t arch_idx = 0; - GetSupportedArchitectureAtIndex(arch_idx, platform_arch); - ++arch_idx) { - if (arch.IsExactMatch(platform_arch)) { - if (compatible_arch_ptr) - *compatible_arch_ptr = platform_arch; - return true; - } - } - } else { - for (uint32_t arch_idx = 0; - GetSupportedArchitectureAtIndex(arch_idx, platform_arch); - ++arch_idx) { - if (arch.IsCompatibleMatch(platform_arch)) { - if (compatible_arch_ptr) - *compatible_arch_ptr = platform_arch; - return true; - } + auto match = exact_arch_match ? &ArchSpec::IsExactMatch + : &ArchSpec::IsCompatibleMatch; + for (const ArchSpec &platform_arch : GetSupportedArchitectures()) { + if ((arch.*match)(platform_arch)) { + if (compatible_arch_ptr) + *compatible_arch_ptr = platform_arch; + return true; } } } @@ -1225,7 +1267,7 @@ Status Platform::PutFile(const FileSpec &source, const FileSpec &destination, LLDB_LOGF(log, "[PutFile] Using block by block transfer....\n"); auto source_open_options = - File::eOpenOptionRead | File::eOpenOptionCloseOnExec; + File::eOpenOptionReadOnly | File::eOpenOptionCloseOnExec; namespace fs = llvm::sys::fs; if (fs::is_symlink_file(source.GetPath())) source_open_options |= File::eOpenOptionDontFollowSymlinks; @@ -1240,7 +1282,7 @@ Status Platform::PutFile(const FileSpec &source, const FileSpec &destination, permissions = lldb::eFilePermissionsFileDefault; lldb::user_id_t dest_file = OpenFile( - destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite | + destination, File::eOpenOptionCanCreate | File::eOpenOptionWriteOnly | File::eOpenOptionTruncate | File::eOpenOptionCloseOnExec, permissions, error); LLDB_LOGF(log, "dest_file = %" PRIu64 "\n", dest_file); @@ -1517,12 +1559,13 @@ const std::vector<ConstString> &Platform::GetTrapHandlerSymbolNames() { return m_trap_handlers; } -Status Platform::GetCachedExecutable( - ModuleSpec &module_spec, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, Platform &remote_platform) { +Status +Platform::GetCachedExecutable(ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr) { const auto platform_spec = module_spec.GetFileSpec(); - const auto error = LoadCachedExecutable( - module_spec, module_sp, module_search_paths_ptr, remote_platform); + const auto error = + LoadCachedExecutable(module_spec, module_sp, module_search_paths_ptr); if (error.Success()) { module_spec.GetFileSpec() = module_sp->GetFileSpec(); module_spec.GetPlatformFileSpec() = platform_spec; @@ -1531,15 +1574,17 @@ Status Platform::GetCachedExecutable( return error; } -Status Platform::LoadCachedExecutable( - const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, Platform &remote_platform) { - return GetRemoteSharedModule(module_spec, nullptr, module_sp, - [&](const ModuleSpec &spec) { - return remote_platform.ResolveExecutable( - spec, module_sp, module_search_paths_ptr); - }, - nullptr); +Status +Platform::LoadCachedExecutable(const ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr) { + return GetRemoteSharedModule( + module_spec, nullptr, module_sp, + [&](const ModuleSpec &spec) { + return ResolveRemoteExecutable(spec, module_sp, + module_search_paths_ptr); + }, + nullptr); } Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec, @@ -1568,9 +1613,8 @@ Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec, // architectures that we should be using (in the correct order) and see if // we can find a match that way ModuleSpec arch_module_spec(module_spec); - for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( - idx, arch_module_spec.GetArchitecture()); - ++idx) { + for (const ArchSpec &arch : GetSupportedArchitectures()) { + arch_module_spec.GetArchitecture() = arch; error = ModuleList::GetSharedModule(arch_module_spec, module_sp, nullptr, nullptr, nullptr); // Did we find an executable using one of the @@ -1619,8 +1663,8 @@ Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec, bool Platform::GetCachedSharedModule(const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, bool *did_create_ptr) { - if (IsHost() || !GetGlobalPlatformProperties()->GetUseModuleCache() || - !GetGlobalPlatformProperties()->GetModuleCacheDirectory()) + if (IsHost() || !GetGlobalPlatformProperties().GetUseModuleCache() || + !GetGlobalPlatformProperties().GetModuleCacheDirectory()) return false; Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); @@ -1663,7 +1707,7 @@ Status Platform::DownloadModuleSlice(const FileSpec &src_file_spec, return error; } - auto src_fd = OpenFile(src_file_spec, File::eOpenOptionRead, + auto src_fd = OpenFile(src_file_spec, File::eOpenOptionReadOnly, lldb::eFilePermissionsFileDefault, error); if (error.Fail()) { @@ -1704,7 +1748,7 @@ Status Platform::DownloadSymbolFile(const lldb::ModuleSP &module_sp, } FileSpec Platform::GetModuleCacheRoot() { - auto dir_spec = GetGlobalPlatformProperties()->GetModuleCacheDirectory(); + auto dir_spec = GetGlobalPlatformProperties().GetModuleCacheDirectory(); dir_spec.AppendPathComponent(GetName().AsCString()); return dir_spec; } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 8ecc66b592ea..84dc2b94a0eb 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -110,6 +110,19 @@ public: } }; +static constexpr OptionEnumValueElement g_follow_fork_mode_values[] = { + { + eFollowParent, + "parent", + "Continue tracing the parent process and detach the child.", + }, + { + eFollowChild, + "child", + "Trace the child process and detach the parent.", + }, +}; + #define LLDB_PROPERTIES_process #include "TargetProperties.inc" @@ -153,10 +166,10 @@ ProcessProperties::ProcessProperties(lldb_private::Process *process) m_collection_sp->Initialize(g_process_properties); m_collection_sp->AppendProperty( ConstString("thread"), ConstString("Settings specific to threads."), - true, Thread::GetGlobalProperties()->GetValueProperties()); + true, Thread::GetGlobalProperties().GetValueProperties()); } else { m_collection_sp = - OptionValueProperties::CreateLocalCopy(*Process::GetGlobalProperties()); + OptionValueProperties::CreateLocalCopy(Process::GetGlobalProperties()); m_collection_sp->SetValueChangedCallback( ePropertyPythonOSPluginPath, [this] { m_process->LoadOperatingSystemPlugin(true); }); @@ -334,6 +347,12 @@ void ProcessProperties::SetOSPluginReportsAllThreads(bool does_report) { nullptr, ePropertyOSPluginReportsAllThreads, does_report); } +FollowForkMode ProcessProperties::GetFollowForkMode() const { + const uint32_t idx = ePropertyFollowForkMode; + return (FollowForkMode)m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_process_properties[idx].default_uint_value); +} + ProcessSP Process::FindPlugin(lldb::TargetSP target_sp, llvm::StringRef plugin_name, ListenerSP listener_sp, @@ -344,9 +363,8 @@ ProcessSP Process::FindPlugin(lldb::TargetSP target_sp, ProcessSP process_sp; ProcessCreateInstance create_callback = nullptr; if (!plugin_name.empty()) { - ConstString const_plugin_name(plugin_name); create_callback = - PluginManager::GetProcessCreateCallbackForPluginName(const_plugin_name); + PluginManager::GetProcessCreateCallbackForPluginName(plugin_name); if (create_callback) { process_sp = create_callback(target_sp, listener_sp, crash_file_path, can_connect); @@ -481,12 +499,12 @@ Process::~Process() { m_thread_list.Clear(); } -const ProcessPropertiesSP &Process::GetGlobalProperties() { +ProcessProperties &Process::GetGlobalProperties() { // NOTE: intentional leak so we don't crash if global destructor chain gets // called as other threads still use the result of this function - static ProcessPropertiesSP *g_settings_sp_ptr = - new ProcessPropertiesSP(new ProcessProperties(nullptr)); - return *g_settings_sp_ptr; + static ProcessProperties *g_settings_ptr = + new ProcessProperties(nullptr); + return *g_settings_ptr; } void Process::Finalize() { @@ -1278,6 +1296,17 @@ StateType Process::GetState() { } void Process::SetPublicState(StateType new_state, bool restarted) { + const bool new_state_is_stopped = StateIsStoppedState(new_state, false); + if (new_state_is_stopped) { + // This will only set the time if the public stop time has no value, so + // it is ok to call this multiple times. With a public stop we can't look + // at the stop ID because many private stops might have happened, so we + // can't check for a stop ID of zero. This allows the "statistics" command + // to dump the time it takes to reach somewhere in your code, like a + // breakpoint you set. + GetTarget().GetStatistics().SetFirstPublicStopTime(); + } + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); LLDB_LOGF(log, "Process::SetPublicState (state = %s, restarted = %i)", @@ -1296,7 +1325,6 @@ void Process::SetPublicState(StateType new_state, bool restarted) { m_public_run_lock.SetStopped(); } else { const bool old_state_is_stopped = StateIsStoppedState(old_state, false); - const bool new_state_is_stopped = StateIsStoppedState(new_state, false); if ((old_state_is_stopped != new_state_is_stopped)) { if (new_state_is_stopped && !restarted) { LLDB_LOGF(log, "Process::SetPublicState (%s) -- unlocking run lock", @@ -1427,7 +1455,9 @@ void Process::SetPrivateState(StateType new_state) { // before we get here. m_thread_list.DidStop(); - m_mod_id.BumpStopID(); + if (m_mod_id.BumpStopID() == 0) + GetTarget().GetStatistics().SetFirstPrivateStopTime(); + if (!m_mod_id.IsLastResumeForUserExpression()) m_mod_id.SetStopEventForLastNaturalStopID(event_sp); m_memory_cache.Clear(); @@ -1953,57 +1983,6 @@ size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str, return out_str.size(); } -size_t Process::ReadStringFromMemory(addr_t addr, char *dst, size_t max_bytes, - Status &error, size_t type_width) { - size_t total_bytes_read = 0; - if (dst && max_bytes && type_width && max_bytes >= type_width) { - // Ensure a null terminator independent of the number of bytes that is - // read. - memset(dst, 0, max_bytes); - size_t bytes_left = max_bytes - type_width; - - const char terminator[4] = {'\0', '\0', '\0', '\0'}; - assert(sizeof(terminator) >= type_width && "Attempting to validate a " - "string with more than 4 bytes " - "per character!"); - - addr_t curr_addr = addr; - const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize(); - char *curr_dst = dst; - - error.Clear(); - while (bytes_left > 0 && error.Success()) { - addr_t cache_line_bytes_left = - cache_line_size - (curr_addr % cache_line_size); - addr_t bytes_to_read = - std::min<addr_t>(bytes_left, cache_line_bytes_left); - size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error); - - if (bytes_read == 0) - break; - - // Search for a null terminator of correct size and alignment in - // bytes_read - size_t aligned_start = total_bytes_read - total_bytes_read % type_width; - for (size_t i = aligned_start; - i + type_width <= total_bytes_read + bytes_read; i += type_width) - if (::memcmp(&dst[i], terminator, type_width) == 0) { - error.Clear(); - return i; - } - - total_bytes_read += bytes_read; - curr_dst += bytes_read; - curr_addr += bytes_read; - bytes_left -= bytes_read; - } - } else { - if (max_bytes) - error.SetErrorString("invalid arguments"); - } - return total_bytes_read; -} - // Deprecated in favor of ReadStringFromMemory which has wchar support and // correct code to find null terminators. size_t Process::ReadCStringFromMemory(addr_t addr, char *dst, @@ -2463,115 +2442,125 @@ Status Process::Launch(ProcessLaunchInfo &launch_info) { m_process_input_reader.reset(); Module *exe_module = GetTarget().GetExecutableModulePointer(); - if (!exe_module) { - error.SetErrorString("executable module does not exist"); - return error; - } - char local_exec_file_path[PATH_MAX]; - char platform_exec_file_path[PATH_MAX]; - exe_module->GetFileSpec().GetPath(local_exec_file_path, - sizeof(local_exec_file_path)); - exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path, - sizeof(platform_exec_file_path)); - if (FileSystem::Instance().Exists(exe_module->GetFileSpec())) { + // The "remote executable path" is hooked up to the local Executable + // module. But we should be able to debug a remote process even if the + // executable module only exists on the remote. However, there needs to + // be a way to express this path, without actually having a module. + // The way to do that is to set the ExecutableFile in the LaunchInfo. + // Figure that out here: + + FileSpec exe_spec_to_use; + if (!exe_module) { + if (!launch_info.GetExecutableFile()) { + error.SetErrorString("executable module does not exist"); + return error; + } + exe_spec_to_use = launch_info.GetExecutableFile(); + } else + exe_spec_to_use = exe_module->GetFileSpec(); + + if (exe_module && FileSystem::Instance().Exists(exe_module->GetFileSpec())) { // Install anything that might need to be installed prior to launching. // For host systems, this will do nothing, but if we are connected to a // remote platform it will install any needed binaries error = GetTarget().Install(&launch_info); if (error.Fail()) return error; + } + // Listen and queue events that are broadcasted during the process launch. + ListenerSP listener_sp(Listener::MakeListener("LaunchEventHijack")); + HijackProcessEvents(listener_sp); + auto on_exit = llvm::make_scope_exit([this]() { RestoreProcessEvents(); }); - // Listen and queue events that are broadcasted during the process launch. - ListenerSP listener_sp(Listener::MakeListener("LaunchEventHijack")); - HijackProcessEvents(listener_sp); - auto on_exit = llvm::make_scope_exit([this]() { RestoreProcessEvents(); }); + if (PrivateStateThreadIsValid()) + PausePrivateStateThread(); - if (PrivateStateThreadIsValid()) - PausePrivateStateThread(); + error = WillLaunch(exe_module); + if (error.Success()) { + const bool restarted = false; + SetPublicState(eStateLaunching, restarted); + m_should_detach = false; - error = WillLaunch(exe_module); - if (error.Success()) { - const bool restarted = false; - SetPublicState(eStateLaunching, restarted); - m_should_detach = false; + if (m_public_run_lock.TrySetRunning()) { + // Now launch using these arguments. + error = DoLaunch(exe_module, launch_info); + } else { + // This shouldn't happen + error.SetErrorString("failed to acquire process run lock"); + } - if (m_public_run_lock.TrySetRunning()) { - // Now launch using these arguments. - error = DoLaunch(exe_module, launch_info); - } else { - // This shouldn't happen - error.SetErrorString("failed to acquire process run lock"); + if (error.Fail()) { + if (GetID() != LLDB_INVALID_PROCESS_ID) { + SetID(LLDB_INVALID_PROCESS_ID); + const char *error_string = error.AsCString(); + if (error_string == nullptr) + error_string = "launch failed"; + SetExitStatus(-1, error_string); } + } else { + EventSP event_sp; - if (error.Fail()) { - if (GetID() != LLDB_INVALID_PROCESS_ID) { - SetID(LLDB_INVALID_PROCESS_ID); - const char *error_string = error.AsCString(); - if (error_string == nullptr) - error_string = "launch failed"; - SetExitStatus(-1, error_string); - } - } else { - EventSP event_sp; - - // Now wait for the process to launch and return control to us, and then - // call DidLaunch: - StateType state = WaitForProcessStopPrivate(event_sp, seconds(10)); + // Now wait for the process to launch and return control to us, and then + // call DidLaunch: + StateType state = WaitForProcessStopPrivate(event_sp, seconds(10)); - if (state == eStateInvalid || !event_sp) { - // We were able to launch the process, but we failed to catch the - // initial stop. - error.SetErrorString("failed to catch stop after launch"); - SetExitStatus(0, "failed to catch stop after launch"); - Destroy(false); - } else if (state == eStateStopped || state == eStateCrashed) { - DidLaunch(); + if (state == eStateInvalid || !event_sp) { + // We were able to launch the process, but we failed to catch the + // initial stop. + error.SetErrorString("failed to catch stop after launch"); + SetExitStatus(0, "failed to catch stop after launch"); + Destroy(false); + } else if (state == eStateStopped || state == eStateCrashed) { + DidLaunch(); - DynamicLoader *dyld = GetDynamicLoader(); - if (dyld) - dyld->DidLaunch(); + DynamicLoader *dyld = GetDynamicLoader(); + if (dyld) + dyld->DidLaunch(); - GetJITLoaders().DidLaunch(); + GetJITLoaders().DidLaunch(); - SystemRuntime *system_runtime = GetSystemRuntime(); - if (system_runtime) - system_runtime->DidLaunch(); + SystemRuntime *system_runtime = GetSystemRuntime(); + if (system_runtime) + system_runtime->DidLaunch(); - if (!m_os_up) - LoadOperatingSystemPlugin(false); + if (!m_os_up) + LoadOperatingSystemPlugin(false); - // We successfully launched the process and stopped, now it the - // right time to set up signal filters before resuming. - UpdateAutomaticSignalFiltering(); + // We successfully launched the process and stopped, now it the + // right time to set up signal filters before resuming. + UpdateAutomaticSignalFiltering(); - // Note, the stop event was consumed above, but not handled. This - // was done to give DidLaunch a chance to run. The target is either - // stopped or crashed. Directly set the state. This is done to - // prevent a stop message with a bunch of spurious output on thread - // status, as well as not pop a ProcessIOHandler. - SetPublicState(state, false); + // Note, the stop event was consumed above, but not handled. This + // was done to give DidLaunch a chance to run. The target is either + // stopped or crashed. Directly set the state. This is done to + // prevent a stop message with a bunch of spurious output on thread + // status, as well as not pop a ProcessIOHandler. + // We are done with the launch hijack listener, and this stop should + // go to the public state listener: + RestoreProcessEvents(); + SetPublicState(state, false); - if (PrivateStateThreadIsValid()) - ResumePrivateStateThread(); - else - StartPrivateStateThread(); + if (PrivateStateThreadIsValid()) + ResumePrivateStateThread(); + else + StartPrivateStateThread(); - // Target was stopped at entry as was intended. Need to notify the - // listeners about it. - if (state == eStateStopped && - launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) - HandlePrivateEvent(event_sp); - } else if (state == eStateExited) { - // We exited while trying to launch somehow. Don't call DidLaunch - // as that's not likely to work, and return an invalid pid. + // Target was stopped at entry as was intended. Need to notify the + // listeners about it. + if (state == eStateStopped && + launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) HandlePrivateEvent(event_sp); - } + } else if (state == eStateExited) { + // We exited while trying to launch somehow. Don't call DidLaunch + // as that's not likely to work, and return an invalid pid. + HandlePrivateEvent(event_sp); } } } else { + std::string local_exec_file_path = exe_spec_to_use.GetPath(); error.SetErrorStringWithFormat("file doesn't exist: '%s'", - local_exec_file_path); + local_exec_file_path.c_str()); } return error; @@ -2625,12 +2614,16 @@ Status Process::LoadCore() { DynamicLoader *Process::GetDynamicLoader() { if (!m_dyld_up) - m_dyld_up.reset(DynamicLoader::FindPlugin(this, nullptr)); + m_dyld_up.reset(DynamicLoader::FindPlugin(this, "")); return m_dyld_up.get(); } DataExtractor Process::GetAuxvData() { return DataExtractor(); } +llvm::Expected<bool> Process::SaveCore(llvm::StringRef outfile) { + return false; +} + JITLoaderList &Process::GetJITLoaders() { if (!m_jit_loaders_up) { m_jit_loaders_up = std::make_unique<JITLoaderList>(); @@ -2916,13 +2909,11 @@ void Process::CompleteAttach() { dyld->DidAttach(); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); - LLDB_LOGF(log, - "Process::%s after DynamicLoader::DidAttach(), target " - "executable is %s (using %s plugin)", - __FUNCTION__, - exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() - : "<none>", - dyld->GetPluginName().AsCString("<unnamed>")); + LLDB_LOG(log, + "after DynamicLoader::DidAttach(), target " + "executable is {0} (using {1} plugin)", + exe_module_sp ? exe_module_sp->GetFileSpec() : FileSpec(), + dyld->GetPluginName()); } } @@ -2933,13 +2924,11 @@ void Process::CompleteAttach() { system_runtime->DidAttach(); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); - LLDB_LOGF(log, - "Process::%s after SystemRuntime::DidAttach(), target " - "executable is %s (using %s plugin)", - __FUNCTION__, - exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() - : "<none>", - system_runtime->GetPluginName().AsCString("<unnamed>")); + LLDB_LOG(log, + "after SystemRuntime::DidAttach(), target " + "executable is {0} (using {1} plugin)", + exe_module_sp ? exe_module_sp->GetFileSpec() : FileSpec(), + system_runtime->GetPluginName()); } } @@ -4310,8 +4299,8 @@ public: : IOHandler(process->GetTarget().GetDebugger(), IOHandler::Type::ProcessIO), m_process(process), - m_read_file(GetInputFD(), File::eOpenOptionRead, false), - m_write_file(write_fd, File::eOpenOptionWrite, false) { + m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false), + m_write_file(write_fd, File::eOpenOptionWriteOnly, false) { m_pipe.CreateNew(false); } @@ -4328,11 +4317,11 @@ public: SetIsDone(false); const int read_fd = m_read_file.GetDescriptor(); - TerminalState terminal_state; - terminal_state.Save(read_fd, false); Terminal terminal(read_fd); - terminal.SetCanonical(false); - terminal.SetEcho(false); + TerminalState terminal_state(terminal, false); + // FIXME: error handling? + llvm::consumeError(terminal.SetCanonical(false)); + llvm::consumeError(terminal.SetEcho(false)); // FD_ZERO, FD_SET are not supported on windows #ifndef _WIN32 const int pipe_read_fd = m_pipe.GetReadFileDescriptor(); @@ -4376,7 +4365,6 @@ public: } m_is_running = false; #endif - terminal_state.Restore(); } void Cancel() override { @@ -4433,7 +4421,7 @@ public: protected: Process *m_process; NativeFile m_read_file; // Read from this file (usually actual STDIN for LLDB - NativeFile m_write_file; // Write to this file (usually the master pty for + NativeFile m_write_file; // Write to this file (usually the primary pty for // getting io to debuggee) Pipe m_pipe; std::atomic<bool> m_is_running{false}; @@ -4494,7 +4482,8 @@ void Process::SettingsInitialize() { Thread::SettingsInitialize(); } void Process::SettingsTerminate() { Thread::SettingsTerminate(); } namespace { -// RestorePlanState is used to record the "is private", "is master" and "okay +// RestorePlanState is used to record the "is private", "is controlling" and +// "okay // to discard" fields of the plan we are running, and reset it on Clean or on // destruction. It will only reset the state once, so you can call Clean and // then monkey with the state and it won't get reset on you again. @@ -4505,7 +4494,7 @@ public: : m_thread_plan_sp(thread_plan_sp), m_already_reset(false) { if (m_thread_plan_sp) { m_private = m_thread_plan_sp->GetPrivate(); - m_is_master = m_thread_plan_sp->IsMasterPlan(); + m_is_controlling = m_thread_plan_sp->IsControllingPlan(); m_okay_to_discard = m_thread_plan_sp->OkayToDiscard(); } } @@ -4516,7 +4505,7 @@ public: if (!m_already_reset && m_thread_plan_sp) { m_already_reset = true; m_thread_plan_sp->SetPrivate(m_private); - m_thread_plan_sp->SetIsMasterPlan(m_is_master); + m_thread_plan_sp->SetIsControllingPlan(m_is_controlling); m_thread_plan_sp->SetOkayToDiscard(m_okay_to_discard); } } @@ -4525,7 +4514,7 @@ private: lldb::ThreadPlanSP m_thread_plan_sp; bool m_already_reset; bool m_private; - bool m_is_master; + bool m_is_controlling; bool m_okay_to_discard; }; } // anonymous namespace @@ -4676,11 +4665,11 @@ Process::RunThreadPlan(ExecutionContext &exe_ctx, thread_plan_sp->SetPrivate(false); - // The plans run with RunThreadPlan also need to be terminal master plans or - // when they are done we will end up asking the plan above us whether we + // The plans run with RunThreadPlan also need to be terminal controlling plans + // or when they are done we will end up asking the plan above us whether we // should stop, which may give the wrong answer. - thread_plan_sp->SetIsMasterPlan(true); + thread_plan_sp->SetIsControllingPlan(true); thread_plan_sp->SetOkayToDiscard(false); // If we are running some utility expression for LLDB, we now have to mark @@ -5864,6 +5853,13 @@ Process::AdvanceAddressToNextBranchInstruction(Address default_stop_addr, return retval; } +Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + if (auto abi = GetABI()) + load_addr = abi->FixDataAddress(load_addr); + return DoGetMemoryRegionInfo(load_addr, range_info); +} + Status Process::GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) { @@ -5963,11 +5959,8 @@ void Process::MapSupportedStructuredDataPlugins( m_structured_data_plugin_map.insert( std::make_pair(type_name, plugin_sp)); names_to_remove.push_back(type_name); - LLDB_LOGF(log, - "Process::%s(): using plugin %s for type name " - "%s", - __FUNCTION__, plugin_sp->GetPluginName().GetCString(), - type_name.GetCString()); + LLDB_LOG(log, "using plugin {0} for type name {1}", + plugin_sp->GetPluginName(), type_name); } } @@ -6091,8 +6084,7 @@ llvm::Expected<const MemoryTagManager *> Process::GetMemoryTagManager() { if (!arch || !tag_manager) { return llvm::createStringError( llvm::inconvertibleErrorCode(), - "This architecture does not support memory tagging", - GetPluginName().GetCString()); + "This architecture does not support memory tagging"); } if (!SupportsMemoryTagging()) { diff --git a/lldb/source/Target/ProcessTrace.cpp b/lldb/source/Target/ProcessTrace.cpp index c878a2ac4eb9..41d5b01b61d8 100644 --- a/lldb/source/Target/ProcessTrace.cpp +++ b/lldb/source/Target/ProcessTrace.cpp @@ -19,12 +19,7 @@ using namespace lldb; using namespace lldb_private; -ConstString ProcessTrace::GetPluginNameStatic() { - static ConstString g_name("trace"); - return g_name; -} - -const char *ProcessTrace::GetPluginDescriptionStatic() { +llvm::StringRef ProcessTrace::GetPluginDescriptionStatic() { return "Trace process plug-in."; } @@ -57,10 +52,6 @@ ProcessTrace::~ProcessTrace() { Finalize(); } -ConstString ProcessTrace::GetPluginName() { return GetPluginNameStatic(); } - -uint32_t ProcessTrace::GetPluginVersion() { return 1; } - void ProcessTrace::DidAttach(ArchSpec &process_arch) { ListenerSP listener_sp( Listener::MakeListener("lldb.process_trace.did_attach_listener")); diff --git a/lldb/source/Target/RegisterContext.cpp b/lldb/source/Target/RegisterContext.cpp index bd50a9486ef3..7364660650e8 100644 --- a/lldb/source/Target/RegisterContext.cpp +++ b/lldb/source/Target/RegisterContext.cpp @@ -54,6 +54,17 @@ RegisterContext::GetRegisterInfoByName(llvm::StringRef reg_name, if (reg_name.empty()) return nullptr; + // Generic register names take precedence over specific register names. + // For example, on x86 we want "sp" to refer to the complete RSP/ESP register + // rather than the 16-bit SP pseudo-register. + uint32_t generic_reg = Args::StringToGenericRegister(reg_name); + if (generic_reg != LLDB_INVALID_REGNUM) { + const RegisterInfo *reg_info = + GetRegisterInfo(eRegisterKindGeneric, generic_reg); + if (reg_info) + return reg_info; + } + const uint32_t num_registers = GetRegisterCount(); for (uint32_t reg = start_idx; reg < num_registers; ++reg) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); @@ -62,45 +73,8 @@ RegisterContext::GetRegisterInfoByName(llvm::StringRef reg_name, reg_name.equals_insensitive(reg_info->alt_name)) return reg_info; } - return nullptr; -} - -uint32_t -RegisterContext::UpdateDynamicRegisterSize(const lldb_private::ArchSpec &arch, - RegisterInfo *reg_info) { - ExecutionContext exe_ctx(CalculateThread()); - // In MIPS, the floating point registers size is depends on FR bit of SR - // register. if SR.FR == 1 then all floating point registers are 64 bits. - // else they are all 32 bits. - - int expr_result; - uint32_t addr_size = arch.GetAddressByteSize(); - const uint8_t *dwarf_opcode_ptr = reg_info->dynamic_size_dwarf_expr_bytes; - const size_t dwarf_opcode_len = reg_info->dynamic_size_dwarf_len; - - DataExtractor dwarf_data(dwarf_opcode_ptr, dwarf_opcode_len, - arch.GetByteOrder(), addr_size); - ModuleSP opcode_ctx; - DWARFExpression dwarf_expr(opcode_ctx, dwarf_data, nullptr); - Value result; - Status error; - if (dwarf_expr.Evaluate(&exe_ctx, this, opcode_ctx, dwarf_data, nullptr, - eRegisterKindDWARF, nullptr, nullptr, result, - &error)) { - expr_result = result.GetScalar().SInt(-1); - switch (expr_result) { - case 0: - return 4; - case 1: - return 8; - default: - return reg_info->byte_size; - } - } else { - printf("Error executing DwarfExpression::Evaluate %s\n", error.AsCString()); - return reg_info->byte_size; - } + return nullptr; } const RegisterInfo *RegisterContext::GetRegisterInfo(lldb::RegisterKind kind, diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 1ce21e6306e0..96b69640a3a3 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -893,13 +893,22 @@ UnwindPlanSP RegisterContextUnwind::GetFullUnwindPlanForFrame() { return arch_default_unwind_plan_sp; } - // If we're in _sigtramp(), unwinding past this frame requires special - // knowledge. On Mac OS X this knowledge is properly encoded in the eh_frame - // section, so prefer that if available. On other platforms we may need to - // provide a platform-specific UnwindPlan which encodes the details of how to - // unwind out of sigtramp. if (m_frame_type == eTrapHandlerFrame && process) { m_fast_unwind_plan_sp.reset(); + + // On some platforms the unwind information for signal handlers is not + // present or correct. Give the platform plugins a chance to provide + // substitute plan. Otherwise, use eh_frame. + if (m_sym_ctx_valid) { + lldb::PlatformSP platform = process->GetTarget().GetPlatform(); + unwind_plan_sp = platform->GetTrapHandlerUnwindPlan( + process->GetTarget().GetArchitecture().GetTriple(), + GetSymbolOrFunctionName(m_sym_ctx)); + + if (unwind_plan_sp) + return unwind_plan_sp; + } + unwind_plan_sp = func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget()); if (!unwind_plan_sp) diff --git a/lldb/source/Target/RemoteAwarePlatform.cpp b/lldb/source/Target/RemoteAwarePlatform.cpp index b0c43ffa839e..eb39fc6db304 100644 --- a/lldb/source/Target/RemoteAwarePlatform.cpp +++ b/lldb/source/Target/RemoteAwarePlatform.cpp @@ -72,8 +72,7 @@ Status RemoteAwarePlatform::ResolveExecutable( } else { if (m_remote_platform_sp) { return GetCachedExecutable(resolved_module_spec, exe_module_sp, - module_search_paths_ptr, - *m_remote_platform_sp); + module_search_paths_ptr); } // We may connect to a process and use the provided executable (Don't use @@ -154,10 +153,10 @@ Status RemoteAwarePlatform::ResolveExecutable( if (error.Fail() || !exe_module_sp) { if (FileSystem::Instance().Readable( resolved_module_spec.GetFileSpec())) { - error.SetErrorStringWithFormat( - "'%s' doesn't contain any '%s' platform architectures: %s", - resolved_module_spec.GetFileSpec().GetPath().c_str(), - GetPluginName().GetCString(), arch_names.GetData()); + error.SetErrorStringWithFormatv( + "'{0}' doesn't contain any '{1}' platform architectures: {2}", + resolved_module_spec.GetFileSpec(), GetPluginName(), + arch_names.GetData()); } else { error.SetErrorStringWithFormat( "'%s' is not readable", @@ -332,18 +331,16 @@ bool RemoteAwarePlatform::GetRemoteOSVersion() { return false; } -bool RemoteAwarePlatform::GetRemoteOSBuildString(std::string &s) { +llvm::Optional<std::string> RemoteAwarePlatform::GetRemoteOSBuildString() { if (m_remote_platform_sp) - return m_remote_platform_sp->GetRemoteOSBuildString(s); - s.clear(); - return false; + return m_remote_platform_sp->GetRemoteOSBuildString(); + return llvm::None; } -bool RemoteAwarePlatform::GetRemoteOSKernelDescription(std::string &s) { +llvm::Optional<std::string> RemoteAwarePlatform::GetRemoteOSKernelDescription() { if (m_remote_platform_sp) - return m_remote_platform_sp->GetRemoteOSKernelDescription(s); - s.clear(); - return false; + return m_remote_platform_sp->GetRemoteOSKernelDescription(); + return llvm::None; } ArchSpec RemoteAwarePlatform::GetRemoteSystemArchitecture() { diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp new file mode 100644 index 000000000000..1b205c533519 --- /dev/null +++ b/lldb/source/Target/Statistics.cpp @@ -0,0 +1,196 @@ +//===-- Statistics.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 "lldb/Target/Statistics.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +static void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, + const std::string &str) { + if (str.empty()) + return; + if (LLVM_LIKELY(llvm::json::isUTF8(str))) + obj.try_emplace(key, str); + else + obj.try_emplace(key, llvm::json::fixUTF8(str)); +} + +json::Value StatsSuccessFail::ToJSON() const { + return json::Object{{"successes", successes}, {"failures", failures}}; +} + +static double elapsed(const StatsTimepoint &start, const StatsTimepoint &end) { + StatsDuration elapsed = end.time_since_epoch() - start.time_since_epoch(); + return elapsed.count(); +} + +void TargetStats::CollectStats(Target &target) { + m_module_identifiers.clear(); + for (ModuleSP module_sp : target.GetImages().Modules()) + m_module_identifiers.emplace_back((intptr_t)module_sp.get()); +} + +json::Value ModuleStats::ToJSON() const { + json::Object module; + EmplaceSafeString(module, "path", path); + EmplaceSafeString(module, "uuid", uuid); + EmplaceSafeString(module, "triple", triple); + module.try_emplace("identifier", identifier); + module.try_emplace("symbolTableParseTime", symtab_parse_time); + module.try_emplace("symbolTableIndexTime", symtab_index_time); + module.try_emplace("debugInfoParseTime", debug_parse_time); + module.try_emplace("debugInfoIndexTime", debug_index_time); + module.try_emplace("debugInfoByteSize", (int64_t)debug_info_size); + return module; +} + +json::Value TargetStats::ToJSON(Target &target) { + CollectStats(target); + + json::Array json_module_uuid_array; + for (auto module_identifier : m_module_identifiers) + json_module_uuid_array.emplace_back(module_identifier); + + json::Object target_metrics_json{ + {m_expr_eval.name, m_expr_eval.ToJSON()}, + {m_frame_var.name, m_frame_var.ToJSON()}, + {"moduleIdentifiers", std::move(json_module_uuid_array)}}; + + if (m_launch_or_attach_time && m_first_private_stop_time) { + double elapsed_time = + elapsed(*m_launch_or_attach_time, *m_first_private_stop_time); + target_metrics_json.try_emplace("launchOrAttachTime", elapsed_time); + } + if (m_launch_or_attach_time && m_first_public_stop_time) { + double elapsed_time = + elapsed(*m_launch_or_attach_time, *m_first_public_stop_time); + target_metrics_json.try_emplace("firstStopTime", elapsed_time); + } + target_metrics_json.try_emplace("targetCreateTime", m_create_time.count()); + + json::Array breakpoints_array; + double totalBreakpointResolveTime = 0.0; + // Rport both the normal breakpoint list and the internal breakpoint list. + for (int i = 0; i < 2; ++i) { + BreakpointList &breakpoints = target.GetBreakpointList(i == 1); + std::unique_lock<std::recursive_mutex> lock; + breakpoints.GetListMutex(lock); + size_t num_breakpoints = breakpoints.GetSize(); + for (size_t i = 0; i < num_breakpoints; i++) { + Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get(); + breakpoints_array.push_back(bp->GetStatistics()); + totalBreakpointResolveTime += bp->GetResolveTime().count(); + } + } + + ProcessSP process_sp = target.GetProcessSP(); + if (process_sp) { + UnixSignalsSP unix_signals_sp = process_sp->GetUnixSignals(); + if (unix_signals_sp) + target_metrics_json.try_emplace("signals", + unix_signals_sp->GetHitCountStatistics()); + uint32_t stop_id = process_sp->GetStopID(); + target_metrics_json.try_emplace("stopCount", stop_id); + } + target_metrics_json.try_emplace("breakpoints", std::move(breakpoints_array)); + target_metrics_json.try_emplace("totalBreakpointResolveTime", + totalBreakpointResolveTime); + + return target_metrics_json; +} + +void TargetStats::SetLaunchOrAttachTime() { + m_launch_or_attach_time = StatsClock::now(); + m_first_private_stop_time = llvm::None; +} + +void TargetStats::SetFirstPrivateStopTime() { + // Launching and attaching has many paths depending on if synchronous mode + // was used or if we are stopping at the entry point or not. Only set the + // first stop time if it hasn't already been set. + if (!m_first_private_stop_time) + m_first_private_stop_time = StatsClock::now(); +} + +void TargetStats::SetFirstPublicStopTime() { + // Launching and attaching has many paths depending on if synchronous mode + // was used or if we are stopping at the entry point or not. Only set the + // first stop time if it hasn't already been set. + if (!m_first_public_stop_time) + m_first_public_stop_time = StatsClock::now(); +} + +bool DebuggerStats::g_collecting_stats = false; + +llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger, + Target *target) { + json::Array json_targets; + json::Array json_modules; + double symtab_parse_time = 0.0; + double symtab_index_time = 0.0; + double debug_parse_time = 0.0; + double debug_index_time = 0.0; + uint64_t debug_info_size = 0; + if (target) { + json_targets.emplace_back(target->ReportStatistics()); + } else { + for (const auto &target : debugger.GetTargetList().Targets()) + json_targets.emplace_back(target->ReportStatistics()); + } + std::vector<ModuleStats> modules; + std::lock_guard<std::recursive_mutex> guard( + Module::GetAllocationModuleCollectionMutex()); + const size_t num_modules = Module::GetNumberAllocatedModules(); + for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { + Module *module = Module::GetAllocatedModuleAtIndex(image_idx); + ModuleStats module_stat; + module_stat.identifier = (intptr_t)module; + module_stat.path = module->GetFileSpec().GetPath(); + if (ConstString object_name = module->GetObjectName()) { + module_stat.path.append(1, '('); + module_stat.path.append(object_name.GetStringRef().str()); + module_stat.path.append(1, ')'); + } + module_stat.uuid = module->GetUUID().GetAsString(); + module_stat.triple = module->GetArchitecture().GetTriple().str(); + module_stat.symtab_parse_time = module->GetSymtabParseTime().count(); + module_stat.symtab_index_time = module->GetSymtabIndexTime().count(); + SymbolFile *sym_file = module->GetSymbolFile(); + if (sym_file) { + module_stat.debug_index_time = sym_file->GetDebugInfoIndexTime().count(); + module_stat.debug_parse_time = sym_file->GetDebugInfoParseTime().count(); + module_stat.debug_info_size = sym_file->GetDebugInfoSize(); + } + symtab_parse_time += module_stat.symtab_parse_time; + symtab_index_time += module_stat.symtab_index_time; + debug_parse_time += module_stat.debug_parse_time; + debug_index_time += module_stat.debug_index_time; + debug_info_size += module_stat.debug_info_size; + json_modules.emplace_back(module_stat.ToJSON()); + } + + json::Object global_stats{ + {"targets", std::move(json_targets)}, + {"modules", std::move(json_modules)}, + {"totalSymbolTableParseTime", symtab_parse_time}, + {"totalSymbolTableIndexTime", symtab_index_time}, + {"totalDebugInfoParseTime", debug_parse_time}, + {"totalDebugInfoIndexTime", debug_index_time}, + {"totalDebugInfoByteSize", debug_info_size}, + }; + return std::move(global_stats); +} diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index aeb97f1919eb..1de281b1761f 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -307,7 +307,7 @@ protected: // There's one other complication here. We may have run an async // breakpoint callback that said we should stop. We only want to - // override that if another breakpoint action says we shouldn't + // override that if another breakpoint action says we shouldn't // stop. If nobody else has an opinion, then we should stop if the // async callback says we should. An example of this is the async // shared library load notification breakpoint and the setting @@ -425,7 +425,7 @@ protected: } internal_breakpoint = bp_loc_sp->GetBreakpoint().IsInternal(); - + // First run the precondition, but since the precondition is per // breakpoint, only run it once per breakpoint. std::pair<std::unordered_set<break_id_t>::iterator, bool> result = @@ -535,7 +535,7 @@ protected: else actually_said_continue = true; } - + // If we are going to stop for this breakpoint, then remove the // breakpoint. if (callback_says_stop && bp_loc_sp && @@ -579,7 +579,7 @@ protected: // Override should_stop decision when we have completed step plan // additionally to the breakpoint m_should_stop = true; - + // We know we're stopping for a completed plan and we don't want to // show the breakpoint stop, so compute the public stop info immediately // here. @@ -615,7 +615,7 @@ public: // performing watchpoint actions. class WatchpointSentry { public: - WatchpointSentry(ProcessSP p_sp, WatchpointSP w_sp) : process_sp(p_sp), + WatchpointSentry(ProcessSP p_sp, WatchpointSP w_sp) : process_sp(p_sp), watchpoint_sp(w_sp) { if (process_sp && watchpoint_sp) { const bool notify = false; @@ -624,7 +624,7 @@ public: process_sp->AddPreResumeAction(SentryPreResumeAction, this); } } - + void DoReenable() { if (process_sp && watchpoint_sp) { bool was_disabled = watchpoint_sp->IsDisabledDuringEphemeralMode(); @@ -637,13 +637,13 @@ public: } } } - + ~WatchpointSentry() { DoReenable(); if (process_sp) process_sp->ClearPreResumeAction(SentryPreResumeAction, this); } - + static bool SentryPreResumeAction(void *sentry_void) { WatchpointSentry *sentry = (WatchpointSentry *) sentry_void; sentry->DoReenable(); @@ -724,14 +724,14 @@ protected: // course of this code. Also by default we're going to stop, so set that // here. m_should_stop = true; - + ThreadSP thread_sp(m_thread_wp.lock()); if (thread_sp) { WatchpointSP wp_sp( thread_sp->CalculateTarget()->GetWatchpointList().FindByID( - GetValue())); + GetValue())); if (wp_sp) { ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); ProcessSP process_sp = exe_ctx.GetProcessSP(); @@ -764,7 +764,7 @@ protected: true, // stop_other_threads new_plan_status)); if (new_plan_sp && new_plan_status.Success()) { - new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetIsControllingPlan(true); new_plan_sp->SetOkayToDiscard(false); new_plan_sp->SetPrivate(true); } @@ -889,12 +889,12 @@ protected: bool old_async = debugger.GetAsyncExecution(); debugger.SetAsyncExecution(true); - + StoppointCallbackContext context(event_ptr, exe_ctx, false); bool stop_requested = wp_sp->InvokeCallback(&context); - + debugger.SetAsyncExecution(old_async); - + // Also make sure that the callback hasn't continued the target. If // it did, when we'll set m_should_stop to false and get out of here. if (HasTargetRunSinceMe()) @@ -1154,6 +1154,103 @@ protected: bool m_performed_action; }; +// StopInfoFork + +class StopInfoFork : public StopInfo { +public: + StopInfoFork(Thread &thread, lldb::pid_t child_pid, lldb::tid_t child_tid) + : StopInfo(thread, child_pid), m_performed_action(false), + m_child_pid(child_pid), m_child_tid(child_tid) {} + + ~StopInfoFork() override = default; + + bool ShouldStop(Event *event_ptr) override { return false; } + + StopReason GetStopReason() const override { return eStopReasonFork; } + + const char *GetDescription() override { return "fork"; } + +protected: + void PerformAction(Event *event_ptr) override { + // Only perform the action once + if (m_performed_action) + return; + m_performed_action = true; + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) + thread_sp->GetProcess()->DidFork(m_child_pid, m_child_tid); + } + + bool m_performed_action; + +private: + lldb::pid_t m_child_pid; + lldb::tid_t m_child_tid; +}; + +// StopInfoVFork + +class StopInfoVFork : public StopInfo { +public: + StopInfoVFork(Thread &thread, lldb::pid_t child_pid, lldb::tid_t child_tid) + : StopInfo(thread, child_pid), m_performed_action(false), + m_child_pid(child_pid), m_child_tid(child_tid) {} + + ~StopInfoVFork() override = default; + + bool ShouldStop(Event *event_ptr) override { return false; } + + StopReason GetStopReason() const override { return eStopReasonVFork; } + + const char *GetDescription() override { return "vfork"; } + +protected: + void PerformAction(Event *event_ptr) override { + // Only perform the action once + if (m_performed_action) + return; + m_performed_action = true; + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) + thread_sp->GetProcess()->DidVFork(m_child_pid, m_child_tid); + } + + bool m_performed_action; + +private: + lldb::pid_t m_child_pid; + lldb::tid_t m_child_tid; +}; + +// StopInfoVForkDone + +class StopInfoVForkDone : public StopInfo { +public: + StopInfoVForkDone(Thread &thread) + : StopInfo(thread, 0), m_performed_action(false) {} + + ~StopInfoVForkDone() override = default; + + bool ShouldStop(Event *event_ptr) override { return false; } + + StopReason GetStopReason() const override { return eStopReasonVForkDone; } + + const char *GetDescription() override { return "vforkdone"; } + +protected: + void PerformAction(Event *event_ptr) override { + // Only perform the action once + if (m_performed_action) + return; + m_performed_action = true; + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) + thread_sp->GetProcess()->DidVForkDone(); + } + + bool m_performed_action; +}; + } // namespace lldb_private StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread, @@ -1175,6 +1272,7 @@ StopInfo::CreateStopReasonWithWatchpointID(Thread &thread, break_id_t watch_id, StopInfoSP StopInfo::CreateStopReasonWithSignal(Thread &thread, int signo, const char *description) { + thread.GetProcess()->GetUnixSignals()->IncrementSignalHitCount(signo); return StopInfoSP(new StopInfoUnixSignal(thread, signo, description)); } @@ -1203,6 +1301,23 @@ StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) { return StopInfoSP(new StopInfoExec(thread)); } +StopInfoSP StopInfo::CreateStopReasonFork(Thread &thread, + lldb::pid_t child_pid, + lldb::tid_t child_tid) { + return StopInfoSP(new StopInfoFork(thread, child_pid, child_tid)); +} + + +StopInfoSP StopInfo::CreateStopReasonVFork(Thread &thread, + lldb::pid_t child_pid, + lldb::tid_t child_tid) { + return StopInfoSP(new StopInfoVFork(thread, child_pid, child_tid)); +} + +StopInfoSP StopInfo::CreateStopReasonVForkDone(Thread &thread) { + return StopInfoSP(new StopInfoVForkDone(thread)); +} + ValueObjectSP StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp) { if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonPlanComplete) { diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 1f8e8c54fa9e..28575b50cf96 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -60,6 +60,7 @@ #include "lldb/Utility/Timer.h" #include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/SetVector.h" #include <memory> #include <mutex> @@ -95,14 +96,10 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, m_watchpoint_list(), m_process_sp(), m_search_filter_sp(), m_image_search_paths(ImageSearchPathsChanged, this), m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0), - m_latest_stop_hook_id(0), - m_valid(true), m_suppress_stop_hooks(false), + m_latest_stop_hook_id(0), m_valid(true), m_suppress_stop_hooks(false), m_is_dummy_target(is_dummy_target), m_frame_recognizer_manager_up( - std::make_unique<StackFrameRecognizerManager>()), - m_stats_storage(static_cast<int>(StatisticKind::StatisticMax)) - -{ + std::make_unique<StackFrameRecognizerManager>()) { SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed"); SetEventName(eBroadcastBitModulesLoaded, "modules-loaded"); SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded"); @@ -1022,7 +1019,7 @@ Status Target::SerializeBreakpointsToFile(const FileSpec &file, } StreamFile out_file(path.c_str(), - File::eOpenOptionTruncate | File::eOpenOptionWrite | + File::eOpenOptionTruncate | File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate | File::eOpenOptionCloseOnExec, lldb::eFilePermissionsFileDefault); @@ -1400,6 +1397,7 @@ void Target::SetExecutableModule(ModuleSP &executable_sp, ClearModules(false); if (executable_sp) { + ElapsedTime elapsed(m_stats.GetCreateTime()); LLDB_SCOPED_TIMERF("Target::SetExecutableModule (executable = '%s')", executable_sp->GetFileSpec().GetPath().c_str()); @@ -1906,6 +1904,68 @@ size_t Target::ReadCStringFromMemory(const Address &addr, char *dst, return total_cstr_len; } +addr_t Target::GetReasonableReadSize(const Address &addr) { + addr_t load_addr = addr.GetLoadAddress(this); + if (load_addr != LLDB_INVALID_ADDRESS && m_process_sp) { + // Avoid crossing cache line boundaries. + addr_t cache_line_size = m_process_sp->GetMemoryCacheLineSize(); + return cache_line_size - (load_addr % cache_line_size); + } + + // The read is going to go to the file cache, so we can just pick a largish + // value. + return 0x1000; +} + +size_t Target::ReadStringFromMemory(const Address &addr, char *dst, + size_t max_bytes, Status &error, + size_t type_width, bool force_live_memory) { + if (!dst || !max_bytes || !type_width || max_bytes < type_width) + return 0; + + size_t total_bytes_read = 0; + + // Ensure a null terminator independent of the number of bytes that is + // read. + memset(dst, 0, max_bytes); + size_t bytes_left = max_bytes - type_width; + + const char terminator[4] = {'\0', '\0', '\0', '\0'}; + assert(sizeof(terminator) >= type_width && "Attempting to validate a " + "string with more than 4 bytes " + "per character!"); + + Address address = addr; + char *curr_dst = dst; + + error.Clear(); + while (bytes_left > 0 && error.Success()) { + addr_t bytes_to_read = + std::min<addr_t>(bytes_left, GetReasonableReadSize(address)); + size_t bytes_read = + ReadMemory(address, curr_dst, bytes_to_read, error, force_live_memory); + + if (bytes_read == 0) + break; + + // Search for a null terminator of correct size and alignment in + // bytes_read + size_t aligned_start = total_bytes_read - total_bytes_read % type_width; + for (size_t i = aligned_start; + i + type_width <= total_bytes_read + bytes_read; i += type_width) + if (::memcmp(&dst[i], terminator, type_width) == 0) { + error.Clear(); + return i; + } + + total_bytes_read += bytes_read; + curr_dst += bytes_read; + address.Slide(bytes_read); + bytes_left -= bytes_read; + } + return total_bytes_read; +} + size_t Target::ReadScalarIntegerFromMemory(const Address &addr, uint32_t byte_size, bool is_signed, Scalar &scalar, Status &error, @@ -2231,7 +2291,10 @@ std::vector<TypeSystem *> Target::GetScratchTypeSystems(bool create_on_demand) { if (!m_valid) return {}; - std::vector<TypeSystem *> scratch_type_systems; + // Some TypeSystem instances are associated with several LanguageTypes so + // they will show up several times in the loop below. The SetVector filters + // out all duplicates as they serve no use for the caller. + llvm::SetVector<TypeSystem *> scratch_type_systems; LanguageSet languages_for_expressions = Language::GetLanguagesSupportingTypeSystemsForExpressions(); @@ -2247,10 +2310,10 @@ std::vector<TypeSystem *> Target::GetScratchTypeSystems(bool create_on_demand) { "system available", Language::GetNameForLanguageType(language)); else - scratch_type_systems.emplace_back(&type_system_or_err.get()); + scratch_type_systems.insert(&type_system_or_err.get()); } - return scratch_type_systems; + return scratch_type_systems.takeVector(); } PersistentExpressionState * @@ -2345,35 +2408,22 @@ void Target::SettingsInitialize() { Process::SettingsInitialize(); } void Target::SettingsTerminate() { Process::SettingsTerminate(); } FileSpecList Target::GetDefaultExecutableSearchPaths() { - TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); - if (properties_sp) - return properties_sp->GetExecutableSearchPaths(); - return FileSpecList(); + return Target::GetGlobalProperties().GetExecutableSearchPaths(); } FileSpecList Target::GetDefaultDebugFileSearchPaths() { - TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); - if (properties_sp) - return properties_sp->GetDebugFileSearchPaths(); - return FileSpecList(); + return Target::GetGlobalProperties().GetDebugFileSearchPaths(); } ArchSpec Target::GetDefaultArchitecture() { - TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); - if (properties_sp) - return properties_sp->GetDefaultArchitecture(); - return ArchSpec(); + return Target::GetGlobalProperties().GetDefaultArchitecture(); } void Target::SetDefaultArchitecture(const ArchSpec &arch) { - TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); - if (properties_sp) { - LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET), - "Target::SetDefaultArchitecture setting target's " - "default architecture to {0} ({1})", - arch.GetArchitectureName(), arch.GetTriple().getTriple()); - return properties_sp->SetDefaultArchitecture(arch); - } + LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET), + "setting target's default architecture to {0} ({1})", + arch.GetArchitectureName(), arch.GetTriple().getTriple()); + Target::GetGlobalProperties().SetDefaultArchitecture(arch); } Target *Target::GetTargetFromContexts(const ExecutionContext *exe_ctx_ptr, @@ -2399,8 +2449,10 @@ ExpressionResults Target::EvaluateExpression( ExpressionResults execution_results = eExpressionSetupError; - if (expr.empty()) + if (expr.empty()) { + m_stats.GetExpressionStats().NotifyFailure(); return execution_results; + } // We shouldn't run stop hooks in expressions. bool old_suppress_value = m_suppress_stop_hooks; @@ -2445,6 +2497,10 @@ ExpressionResults Target::EvaluateExpression( fixed_expression, ctx_obj); } + if (execution_results == eExpressionCompleted) + m_stats.GetExpressionStats().NotifySuccess(); + else + m_stats.GetExpressionStats().NotifyFailure(); return execution_results; } @@ -2768,12 +2824,12 @@ bool Target::RunStopHooks() { return false; } -const TargetPropertiesSP &Target::GetGlobalProperties() { +TargetProperties &Target::GetGlobalProperties() { // NOTE: intentional leak so we don't crash if global destructor chain gets // called as other threads still use the result of this function - static TargetPropertiesSP *g_settings_sp_ptr = - new TargetPropertiesSP(new TargetProperties(nullptr)); - return *g_settings_sp_ptr; + static TargetProperties *g_settings_ptr = + new TargetProperties(nullptr); + return *g_settings_ptr; } Status Target::Install(ProcessLaunchInfo *launch_info) { @@ -2908,6 +2964,7 @@ bool Target::SetSectionUnloaded(const lldb::SectionSP §ion_sp, void Target::ClearAllLoadedSections() { m_section_load_history.Clear(); } Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { + m_stats.SetLaunchOrAttachTime(); Status error; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); @@ -2936,17 +2993,9 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { launch_info.GetFlags().Set(eLaunchFlagDebug); if (launch_info.IsScriptedProcess()) { - TargetPropertiesSP properties_sp = GetGlobalProperties(); - - if (!properties_sp) { - LLDB_LOGF(log, "Target::%s Couldn't fetch target global properties.", - __FUNCTION__); - return error; - } - // Only copy scripted process launch options. - ProcessLaunchInfo &default_launch_info = - const_cast<ProcessLaunchInfo &>(properties_sp->GetProcessLaunchInfo()); + ProcessLaunchInfo &default_launch_info = const_cast<ProcessLaunchInfo &>( + GetGlobalProperties().GetProcessLaunchInfo()); default_launch_info.SetProcessPluginName("ScriptedProcess"); default_launch_info.SetScriptedProcessClassName( @@ -2993,7 +3042,7 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { DeleteCurrentProcess(); m_process_sp = - GetPlatform()->DebugProcess(launch_info, debugger, this, error); + GetPlatform()->DebugProcess(launch_info, debugger, *this, error); } else { LLDB_LOGF(log, @@ -3119,6 +3168,7 @@ llvm::Expected<TraceSP> Target::GetTraceOrCreate() { } Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) { + m_stats.SetLaunchOrAttachTime(); auto state = eStateInvalid; auto process_sp = GetProcessSP(); if (process_sp) { @@ -3731,7 +3781,7 @@ TargetProperties::TargetProperties(Target *target) : Properties(), m_launch_info(), m_target(target) { if (target) { m_collection_sp = - OptionValueProperties::CreateLocalCopy(*Target::GetGlobalProperties()); + OptionValueProperties::CreateLocalCopy(Target::GetGlobalProperties()); // Set callbacks to update launch_info whenever "settins set" updated any // of these properties @@ -3781,7 +3831,7 @@ TargetProperties::TargetProperties(Target *target) true, m_experimental_properties_up->GetValueProperties()); m_collection_sp->AppendProperty( ConstString("process"), ConstString("Settings specific to processes."), - true, Process::GetGlobalProperties()->GetValueProperties()); + true, Process::GetGlobalProperties().GetValueProperties()); } } @@ -3985,6 +4035,45 @@ Environment TargetProperties::GetEnvironment() const { return ComputeEnvironment(); } +Environment TargetProperties::GetInheritedEnvironment() const { + Environment environment; + + if (m_target == nullptr) + return environment; + + if (!m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, ePropertyInheritEnv, + g_target_properties[ePropertyInheritEnv].default_uint_value != 0)) + return environment; + + PlatformSP platform_sp = m_target->GetPlatform(); + if (platform_sp == nullptr) + return environment; + + Environment platform_environment = platform_sp->GetEnvironment(); + for (const auto &KV : platform_environment) + environment[KV.first()] = KV.second; + + Args property_unset_environment; + m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyUnsetEnvVars, + property_unset_environment); + for (const auto &var : property_unset_environment) + environment.erase(var.ref()); + + return environment; +} + +Environment TargetProperties::GetTargetEnvironment() const { + Args property_environment; + m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyEnvVars, + property_environment); + Environment environment; + for (const auto &KV : Environment(property_environment)) + environment[KV.first()] = KV.second; + + return environment; +} + void TargetProperties::SetEnvironment(Environment env) { // TODO: Get rid of the Args intermediate step const uint32_t idx = ePropertyEnvVars; @@ -4249,16 +4338,6 @@ void TargetProperties::SetDisplayRecognizedArguments(bool b) { m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } -bool TargetProperties::GetNonStopModeEnabled() const { - const uint32_t idx = ePropertyNonStopModeEnabled; - return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); -} - -void TargetProperties::SetNonStopModeEnabled(bool b) { - const uint32_t idx = ePropertyNonStopModeEnabled; - m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); -} - const ProcessLaunchInfo &TargetProperties::GetProcessLaunchInfo() const { return m_launch_info; } @@ -4435,3 +4514,6 @@ std::recursive_mutex &Target::GetAPIMutex() { else return m_mutex; } + +/// Get metrics associated with this target in JSON format. +llvm::json::Value Target::ReportStatistics() { return m_stats.ToJSON(*this); } diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td index 8f627ad0f1a8..063ba0a6c25a 100644 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -163,9 +163,6 @@ let Definition = "target" in { def DisplayRecognizedArguments: Property<"display-recognized-arguments", "Boolean">, DefaultFalse, Desc<"Show recognized arguments in variable listings by default.">; - def NonStopModeEnabled: Property<"non-stop-mode", "Boolean">, - DefaultFalse, - Desc<"Disable lock-step debugging, instead control threads independently.">; def RequireHardwareBreakpoints: Property<"require-hardware-breakpoint", "Boolean">, DefaultFalse, Desc<"Require all breakpoints to be hardware breakpoints.">; @@ -239,6 +236,10 @@ let Definition = "process" in { def VirtualAddressableBits: Property<"virtual-addressable-bits", "UInt64">, DefaultUnsignedValue<0>, Desc<"The number of bits used for addressing. If the value is 39, then bits 0..38 are used for addressing. The default value of 0 means unspecified.">; + def FollowForkMode: Property<"follow-fork-mode", "Enum">, + DefaultEnumValue<"eFollowParent">, + EnumValues<"OptionEnumValues(g_follow_fork_mode_values)">, + Desc<"Debugger's behavior upon fork or vfork.">; } let Definition = "platform" in { diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index b423f1b5f1fe..1b32331d98f7 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -55,12 +55,11 @@ using namespace lldb; using namespace lldb_private; -const ThreadPropertiesSP &Thread::GetGlobalProperties() { +ThreadProperties &Thread::GetGlobalProperties() { // NOTE: intentional leak so we don't crash if global destructor chain gets // called as other threads still use the result of this function - static ThreadPropertiesSP *g_settings_sp_ptr = - new ThreadPropertiesSP(new ThreadProperties(true)); - return *g_settings_sp_ptr; + static ThreadProperties *g_settings_ptr = new ThreadProperties(true); + return *g_settings_ptr; } #define LLDB_PROPERTIES_thread @@ -103,7 +102,7 @@ ThreadProperties::ThreadProperties(bool is_global) : Properties() { m_collection_sp->Initialize(g_thread_properties); } else m_collection_sp = - OptionValueProperties::CreateLocalCopy(*Thread::GetGlobalProperties()); + OptionValueProperties::CreateLocalCopy(Thread::GetGlobalProperties()); } ThreadProperties::~ThreadProperties() = default; @@ -845,7 +844,7 @@ bool Thread::ShouldStop(Event *event_ptr) { // we're done, otherwise we forward this to the next plan in the // stack below. done_processing_current_plan = - (plan_ptr->IsMasterPlan() && !plan_ptr->OkayToDiscard()); + (plan_ptr->IsControllingPlan() && !plan_ptr->OkayToDiscard()); } else done_processing_current_plan = true; @@ -883,11 +882,11 @@ bool Thread::ShouldStop(Event *event_ptr) { current_plan->GetName()); } - // If a Master Plan wants to stop, we let it. Otherwise, see if the - // plan's parent wants to stop. + // If a Controlling Plan wants to stop, we let it. Otherwise, see if + // the plan's parent wants to stop. PopPlan(); - if (should_stop && current_plan->IsMasterPlan() && + if (should_stop && current_plan->IsControllingPlan() && !current_plan->OkayToDiscard()) { break; } @@ -906,8 +905,8 @@ bool Thread::ShouldStop(Event *event_ptr) { should_stop = false; } - // One other potential problem is that we set up a master plan, then stop in - // before it is complete - for instance by hitting a breakpoint during a + // One other potential problem is that we set up a controlling plan, then stop + // in before it is complete - for instance by hitting a breakpoint during a // step-over - then do some step/finish/etc operations that wind up past the // end point condition of the initial plan. We don't want to strand the // original plan on the stack, This code clears stale plans off the stack. @@ -1215,7 +1214,7 @@ void Thread::DiscardThreadPlans(bool force) { GetPlans().DiscardAllPlans(); return; } - GetPlans().DiscardConsultingMasterPlans(); + GetPlans().DiscardConsultingControllingPlans(); } Status Thread::UnwindInnermostExpression() { @@ -1915,7 +1914,7 @@ Status Thread::StepIn(bool source_step, false, abort_other_plans, run_mode, error); } - new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetIsControllingPlan(true); new_plan_sp->SetOkayToDiscard(false); // Why do we need to set the current thread by ID here??? @@ -1948,7 +1947,7 @@ Status Thread::StepOver(bool source_step, true, abort_other_plans, run_mode, error); } - new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetIsControllingPlan(true); new_plan_sp->SetOkayToDiscard(false); // Why do we need to set the current thread by ID here??? @@ -1972,7 +1971,7 @@ Status Thread::StepOut() { abort_other_plans, nullptr, first_instruction, stop_other_threads, eVoteYes, eVoteNoOpinion, 0, error)); - new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetIsControllingPlan(true); new_plan_sp->SetOkayToDiscard(false); // Why do we need to set the current thread by ID here??? diff --git a/lldb/source/Target/ThreadPlan.cpp b/lldb/source/Target/ThreadPlan.cpp index 6b55f3912d11..3b42831f1fbf 100644 --- a/lldb/source/Target/ThreadPlan.cpp +++ b/lldb/source/Target/ThreadPlan.cpp @@ -26,8 +26,8 @@ ThreadPlan::ThreadPlan(ThreadPlanKind kind, const char *name, Thread &thread, m_takes_iteration_count(false), m_could_not_resolve_hw_bp(false), m_thread(&thread), m_kind(kind), m_name(name), m_plan_complete_mutex(), m_cached_plan_explains_stop(eLazyBoolCalculate), m_plan_complete(false), - m_plan_private(false), m_okay_to_discard(true), m_is_master_plan(false), - m_plan_succeeded(true) { + m_plan_private(false), m_okay_to_discard(true), + m_is_controlling_plan(false), m_plan_succeeded(true) { SetID(GetNextID()); } @@ -149,10 +149,10 @@ lldb::user_id_t ThreadPlan::GetNextID() { void ThreadPlan::DidPush() {} -void ThreadPlan::WillPop() {} +void ThreadPlan::DidPop() {} bool ThreadPlan::OkayToDiscard() { - return IsMasterPlan() ? m_okay_to_discard : true; + return IsControllingPlan() ? m_okay_to_discard : true; } lldb::StateType ThreadPlan::RunState() { diff --git a/lldb/source/Target/ThreadPlanBase.cpp b/lldb/source/Target/ThreadPlanBase.cpp index c6c4d97c1655..46ae9c32a0de 100644 --- a/lldb/source/Target/ThreadPlanBase.cpp +++ b/lldb/source/Target/ThreadPlanBase.cpp @@ -40,7 +40,7 @@ ThreadPlanBase::ThreadPlanBase(Thread &thread) #endif new_tracer_sp->EnableTracing(thread.GetTraceEnabledState()); SetThreadPlanTracer(new_tracer_sp); - SetIsMasterPlan(true); + SetIsControllingPlan(true); } ThreadPlanBase::~ThreadPlanBase() = default; @@ -90,8 +90,8 @@ bool ThreadPlanBase::ShouldStop(Event *event_ptr) { case eStopReasonWatchpoint: if (stop_info_sp->ShouldStopSynchronous(event_ptr)) { // If we are going to stop for a breakpoint, then unship the other - // plans at this point. Don't force the discard, however, so Master - // plans can stay in place if they want to. + // plans at this point. Don't force the discard, however, so + // Controlling plans can stay in place if they want to. LLDB_LOGF( log, "Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index 3699a507d058..0336a9daf10a 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -33,7 +33,7 @@ using namespace lldb_private; bool ThreadPlanCallFunction::ConstructorSetup( Thread &thread, ABI *&abi, lldb::addr_t &start_load_addr, lldb::addr_t &function_load_addr) { - SetIsMasterPlan(true); + SetIsControllingPlan(true); SetOkayToDiscard(false); SetPrivate(true); @@ -209,7 +209,7 @@ void ThreadPlanCallFunction::DoTakedown(bool success) { } } -void ThreadPlanCallFunction::WillPop() { DoTakedown(PlanSucceeded()); } +void ThreadPlanCallFunction::DidPop() { DoTakedown(PlanSucceeded()); } void ThreadPlanCallFunction::GetDescription(Stream *s, DescriptionLevel level) { if (level == eDescriptionLevelBrief) { diff --git a/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp b/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp index 7471e9b3d7ac..4bccf96d721b 100644 --- a/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp +++ b/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp @@ -18,7 +18,7 @@ ThreadPlanCallOnFunctionExit::ThreadPlanCallOnFunctionExit( ), m_callback(callback) { // We are not a user-generated plan. - SetIsMasterPlan(false); + SetIsControllingPlan(false); } void ThreadPlanCallOnFunctionExit::DidPush() { diff --git a/lldb/source/Target/ThreadPlanCallUserExpression.cpp b/lldb/source/Target/ThreadPlanCallUserExpression.cpp index 9dddd850b6ab..d833a4d7ed27 100644 --- a/lldb/source/Target/ThreadPlanCallUserExpression.cpp +++ b/lldb/source/Target/ThreadPlanCallUserExpression.cpp @@ -39,7 +39,7 @@ ThreadPlanCallUserExpression::ThreadPlanCallUserExpression( m_user_expression_sp(user_expression_sp) { // User expressions are generally "User generated" so we should set them up // to stop when done. - SetIsMasterPlan(true); + SetIsControllingPlan(true); SetOkayToDiscard(false); } @@ -59,8 +59,8 @@ void ThreadPlanCallUserExpression::DidPush() { m_user_expression_sp->WillStartExecuting(); } -void ThreadPlanCallUserExpression::WillPop() { - ThreadPlanCallFunction::WillPop(); +void ThreadPlanCallUserExpression::DidPop() { + ThreadPlanCallFunction::DidPop(); if (m_user_expression_sp) m_user_expression_sp.reset(); } diff --git a/lldb/source/Target/ThreadPlanPython.cpp b/lldb/source/Target/ThreadPlanPython.cpp index e83f0e9e715e..cd63d28a3934 100644 --- a/lldb/source/Target/ThreadPlanPython.cpp +++ b/lldb/source/Target/ThreadPlanPython.cpp @@ -31,7 +31,7 @@ ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name, eVoteNoOpinion, eVoteNoOpinion), m_class_name(class_name), m_args_data(args_data), m_did_push(false), m_stop_others(false) { - SetIsMasterPlan(true); + SetIsControllingPlan(true); SetOkayToDiscard(true); SetPrivate(false); } diff --git a/lldb/source/Target/ThreadPlanStack.cpp b/lldb/source/Target/ThreadPlanStack.cpp index d25602d25b91..f09583cc50cc 100644 --- a/lldb/source/Target/ThreadPlanStack.cpp +++ b/lldb/source/Target/ThreadPlanStack.cpp @@ -150,10 +150,13 @@ lldb::ThreadPlanSP ThreadPlanStack::PopPlan() { std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); assert(m_plans.size() > 1 && "Can't pop the base thread plan"); - lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); - m_completed_plans.push_back(plan_sp); - plan_sp->WillPop(); + // Note that moving the top element of the vector would leave it in an + // undefined state, and break the guarantee that the stack's thread plans are + // all valid. + lldb::ThreadPlanSP plan_sp = m_plans.back(); m_plans.pop_back(); + m_completed_plans.push_back(plan_sp); + plan_sp->DidPop(); return plan_sp; } @@ -161,10 +164,13 @@ lldb::ThreadPlanSP ThreadPlanStack::DiscardPlan() { std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); assert(m_plans.size() > 1 && "Can't discard the base thread plan"); - lldb::ThreadPlanSP plan_sp = std::move(m_plans.back()); - m_discarded_plans.push_back(plan_sp); - plan_sp->WillPop(); + // Note that moving the top element of the vector would leave it in an + // undefined state, and break the guarantee that the stack's thread plans are + // all valid. + lldb::ThreadPlanSP plan_sp = m_plans.back(); m_plans.pop_back(); + m_discarded_plans.push_back(plan_sp); + plan_sp->DidPop(); return plan_sp; } @@ -207,35 +213,35 @@ void ThreadPlanStack::DiscardAllPlans() { return; } -void ThreadPlanStack::DiscardConsultingMasterPlans() { +void ThreadPlanStack::DiscardConsultingControllingPlans() { std::lock_guard<std::recursive_mutex> guard(m_stack_mutex); while (true) { - int master_plan_idx; + int controlling_plan_idx; bool discard = true; - // Find the first master plan, see if it wants discarding, and if yes + // Find the first controlling plan, see if it wants discarding, and if yes // discard up to it. - for (master_plan_idx = m_plans.size() - 1; master_plan_idx >= 0; - master_plan_idx--) { - if (m_plans[master_plan_idx]->IsMasterPlan()) { - discard = m_plans[master_plan_idx]->OkayToDiscard(); + for (controlling_plan_idx = m_plans.size() - 1; controlling_plan_idx >= 0; + controlling_plan_idx--) { + if (m_plans[controlling_plan_idx]->IsControllingPlan()) { + discard = m_plans[controlling_plan_idx]->OkayToDiscard(); break; } } - // If the master plan doesn't want to get discarded, then we're done. + // If the controlling plan doesn't want to get discarded, then we're done. if (!discard) return; // First pop all the dependent plans: - for (int i = m_plans.size() - 1; i > master_plan_idx; i--) { + for (int i = m_plans.size() - 1; i > controlling_plan_idx; i--) { DiscardPlan(); } - // Now discard the master plan itself. + // Now discard the controlling plan itself. // The bottom-most plan never gets discarded. "OkayToDiscard" for it // means discard it's dependent plans, but not it... - if (master_plan_idx > 0) { + if (controlling_plan_idx > 0) { DiscardPlan(); } } diff --git a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp index 965a7b3a9960..f007b0fa9371 100644 --- a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp +++ b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -124,9 +124,7 @@ bool ThreadPlanStepOverBreakpoint::WillStop() { return true; } -void ThreadPlanStepOverBreakpoint::WillPop() { - ReenableBreakpointSite(); -} +void ThreadPlanStepOverBreakpoint::DidPop() { ReenableBreakpointSite(); } bool ThreadPlanStepOverBreakpoint::MischiefManaged() { lldb::addr_t pc_addr = GetThread().GetRegisterContext()->GetPC(); diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp index 827f3264c096..38b3a7cb006d 100644 --- a/lldb/source/Target/Trace.cpp +++ b/lldb/source/Target/Trace.cpp @@ -67,30 +67,28 @@ Trace::FindPluginForPostMortemProcess(Debugger &debugger, if (!json::fromJSON(trace_session_file, json_session, root)) return root.getError(); - ConstString plugin_name(json_session.trace.type); - if (auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name)) + if (auto create_callback = + PluginManager::GetTraceCreateCallback(json_session.trace.type)) return create_callback(trace_session_file, session_file_dir, debugger); return createInvalidPlugInError(json_session.trace.type); } -Expected<lldb::TraceSP> -Trace::FindPluginForLiveProcess(llvm::StringRef plugin_name, Process &process) { +Expected<lldb::TraceSP> Trace::FindPluginForLiveProcess(llvm::StringRef name, + Process &process) { if (!process.IsLiveDebugSession()) return createStringError(inconvertibleErrorCode(), "Can't trace non-live processes"); - ConstString name(plugin_name); if (auto create_callback = PluginManager::GetTraceCreateCallbackForLiveProcess(name)) return create_callback(process); - return createInvalidPlugInError(plugin_name); + return createInvalidPlugInError(name); } Expected<StringRef> Trace::FindPluginSchema(StringRef name) { - ConstString plugin_name(name); - StringRef schema = PluginManager::GetTraceSchema(plugin_name); + StringRef schema = PluginManager::GetTraceSchema(name); if (!schema.empty()) return schema; @@ -108,23 +106,21 @@ Error Trace::Stop() { if (!m_live_process) return createStringError(inconvertibleErrorCode(), "Tracing requires a live process."); - return m_live_process->TraceStop( - TraceStopRequest(GetPluginName().AsCString())); + return m_live_process->TraceStop(TraceStopRequest(GetPluginName())); } Error Trace::Stop(llvm::ArrayRef<lldb::tid_t> tids) { if (!m_live_process) return createStringError(inconvertibleErrorCode(), "Tracing requires a live process."); - return m_live_process->TraceStop( - TraceStopRequest(GetPluginName().AsCString(), tids)); + return m_live_process->TraceStop(TraceStopRequest(GetPluginName(), tids)); } Expected<std::string> Trace::GetLiveProcessState() { if (!m_live_process) return createStringError(inconvertibleErrorCode(), "Tracing requires a live process."); - return m_live_process->TraceGetState(GetPluginName().AsCString()); + return m_live_process->TraceGetState(GetPluginName()); } Optional<size_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, @@ -158,7 +154,7 @@ Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) { "Tracing data \"%s\" is not available for thread %" PRIu64 ".", kind.data(), tid); - TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(), + TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), static_cast<int64_t>(tid), 0, static_cast<int64_t>(*size)}; return m_live_process->TraceGetBinaryData(request); @@ -175,8 +171,8 @@ Trace::GetLiveProcessBinaryData(llvm::StringRef kind) { inconvertibleErrorCode(), "Tracing data \"%s\" is not available for the process.", kind.data()); - TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(), - None, 0, static_cast<int64_t>(*size)}; + TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), None, 0, + static_cast<int64_t>(*size)}; return m_live_process->TraceGetBinaryData(request); } diff --git a/lldb/source/Target/TraceExporter.cpp b/lldb/source/Target/TraceExporter.cpp index 1a6571dba4a0..8c925aa495b0 100644 --- a/lldb/source/Target/TraceExporter.cpp +++ b/lldb/source/Target/TraceExporter.cpp @@ -22,11 +22,10 @@ static Error createInvalidPlugInError(StringRef plugin_name) { } Expected<lldb::TraceExporterUP> -TraceExporter::FindPlugin(llvm::StringRef plugin_name) { - ConstString name(plugin_name); +TraceExporter::FindPlugin(llvm::StringRef name) { if (auto create_callback = PluginManager::GetTraceExporterCreateCallback(name)) return create_callback(); - return createInvalidPlugInError(plugin_name); + return createInvalidPlugInError(name); } diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp index 4ec2e25c7e3b..26ff0bbd3825 100644 --- a/lldb/source/Target/UnixSignals.cpp +++ b/lldb/source/Target/UnixSignals.cpp @@ -12,10 +12,10 @@ #include "Plugins/Process/Utility/MipsLinuxSignals.h" #include "Plugins/Process/Utility/NetBSDSignals.h" #include "lldb/Host/HostInfo.h" -#include "lldb/Host/StringConvert.h" #include "lldb/Utility/ArchSpec.h" using namespace lldb_private; +using namespace llvm; UnixSignals::Signal::Signal(const char *name, bool default_suppress, bool default_stop, bool default_notify, @@ -156,9 +156,8 @@ int32_t UnixSignals::GetSignalNumberFromName(const char *name) const { return pos->first; } - const int32_t signo = - StringConvert::ToSInt32(name, LLDB_INVALID_SIGNAL_NUMBER, 0); - if (signo != LLDB_INVALID_SIGNAL_NUMBER) + int32_t signo; + if (llvm::to_integer(name, signo)) return signo; return LLDB_INVALID_SIGNAL_NUMBER; } @@ -314,3 +313,20 @@ UnixSignals::GetFilteredSignals(llvm::Optional<bool> should_suppress, return result; } + +void UnixSignals::IncrementSignalHitCount(int signo) { + collection::iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) + pos->second.m_hit_count += 1; +} + +json::Value UnixSignals::GetHitCountStatistics() const { + json::Array json_signals; + for (const auto &pair: m_signals) { + if (pair.second.m_hit_count > 0) + json_signals.emplace_back(json::Object{ + { pair.second.m_name.GetCString(), pair.second.m_hit_count } + }); + } + return std::move(json_signals); +} |
