mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Add an extra opaque field to AddressSpace, which can be used by clients of RemoteInspection to distinguish between different address spaces. LLDB employs an optimization where it reads memory from files instead of the running process whenever it can to speed up memory reads (these can be slow when debugging something over a network). To do this, it needs to keep track whether an address originated from a process or a file. It currently distinguishes addresses by setting an unused high bit on the address, but because of pointer authentication this is not a reliable solution. In order to keep this optimization working, this patch adds an extra opaque AddressSpace field to RemoteAddress, which LLDB can use on its own implementation of MemoryReader to distinguish between addresses. This patch is NFC for the other RemoteInspection clients, as it adds extra information to RemoteAddress, which is entirely optional and if unused should not change the behavior of the library. Although this patch is quite big the changes are largely mechanical, replacing threading StoredPointer with RemoteAddress. rdar://148361743
339 lines
10 KiB
C++
339 lines
10 KiB
C++
//===--- Failure.h - Failure to access remote memory ------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file provides a simple diagnostics library for the various
|
|
// operations for working with a remote process.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_REMOTE_FAILURE_H
|
|
#define SWIFT_REMOTE_FAILURE_H
|
|
|
|
#include "swift/Remote/RemoteAddress.h"
|
|
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <cstring>
|
|
#include <type_traits>
|
|
|
|
namespace swift {
|
|
namespace remote {
|
|
|
|
class Failure {
|
|
public:
|
|
/// An enum which unifies all of the failure kinds into a single namespace.
|
|
/// This is how kinds are stored internally.
|
|
enum class Kind {
|
|
#define FAILURE(KIND, TEXT, ARGTYS) KIND,
|
|
#include "swift/Remote/FailureKinds.def"
|
|
};
|
|
|
|
/// A bunch of enums for all the different kinds of failure.
|
|
/// Users should construct a Failure by passing Failure::KIND instead
|
|
/// of Failure::Kind::KIND.
|
|
#define FAILURE(KIND, TEXT, ARGTYS) \
|
|
enum KIND##_t { KIND = unsigned(Kind::KIND) };
|
|
#include "swift/Remote/FailureKinds.def"
|
|
|
|
private:
|
|
// A whole lot of template machinery to validate that the right argument
|
|
// types were passed:
|
|
|
|
// A sequence of types.
|
|
template <class... Tys> struct TypeBundle {};
|
|
|
|
// A way of turning (X, Y, Z) into a TypeBundle:
|
|
// typename TypeBundleForFunctionTypeParameters<void ARGS>::types
|
|
// Partially specialized at the end of this file.
|
|
template <class FnTy> struct TypeBundleForFunctionTypeParameters;
|
|
|
|
// A way of getting the expected TypeBundle of (nonce) argument types
|
|
// for a particular failure kind:
|
|
// typename ArgTypesForFailureKind<KindTy>::types
|
|
// Explicitly specialized for each kind type at the end of this file.
|
|
template <class KindTy> struct ArgTypesForFailureKind;
|
|
|
|
// Nonce expected argument types.
|
|
enum ArgType_String {};
|
|
enum ArgType_Address {};
|
|
|
|
// A predicate that decides whether the given argument type satisfies
|
|
// the given nonce expected argument type.
|
|
template <class Expected, class Actual> struct IsAcceptableArgType {
|
|
static constexpr bool value = false;
|
|
};
|
|
|
|
template <class... Ts>
|
|
static void validateFailureArgsRecursive(TypeBundle<Ts...> expected,
|
|
TypeBundle<> actual) {
|
|
static_assert(std::is_same<decltype(expected), decltype(actual)>::value,
|
|
"too few arguments to diagnostic");
|
|
}
|
|
|
|
template <class Expected, class... RestOfExpected,
|
|
class Actual, class... RestOfActual>
|
|
static void validateFailureArgsRecursive(
|
|
TypeBundle<Expected, RestOfExpected...> expected,
|
|
TypeBundle<Actual, RestOfActual...> actual) {
|
|
using SimplifiedActual = typename std::decay<Actual>::type;
|
|
static_assert(IsAcceptableArgType<Expected, SimplifiedActual>::value,
|
|
"argument does no match");
|
|
validateFailureArgsRecursive(TypeBundle<RestOfExpected...>(),
|
|
TypeBundle<RestOfActual...>());
|
|
}
|
|
|
|
public:
|
|
/// Validate that it's okay to construct a Failure with the given arguments.
|
|
template <class KindTy, class... ArgTys>
|
|
static void validateFailureArgs(KindTy ty, ArgTys &&... argTys) {
|
|
using ExpectedArgTypes = typename ArgTypesForFailureKind<KindTy>::types;
|
|
validateFailureArgsRecursive(ExpectedArgTypes(), TypeBundle<ArgTys...>());
|
|
}
|
|
|
|
private:
|
|
static const char *getTextForKind(Kind kind) {
|
|
switch (kind) {
|
|
#define FAILURE(KIND, TEXT, ARGTYS) \
|
|
case Kind::KIND: return TEXT;
|
|
#include "swift/Remote/FailureKinds.def"
|
|
}
|
|
|
|
llvm_unreachable("Unhandled FailureKind in switch.");
|
|
}
|
|
|
|
union ArgStorage {
|
|
std::string String;
|
|
RemoteAddress Address;
|
|
|
|
ArgStorage() {}
|
|
ArgStorage(const ArgStorage &other) = delete;
|
|
ArgStorage &operator=(const ArgStorage &other) = delete;
|
|
~ArgStorage() {}
|
|
};
|
|
enum class ArgStorageKind : char {
|
|
None,
|
|
String, // std::string
|
|
Address, // RemoteAddress
|
|
};
|
|
|
|
enum {
|
|
MaxArgs = 4
|
|
};
|
|
|
|
Kind TheKind;
|
|
ArgStorageKind ArgKinds[MaxArgs];
|
|
ArgStorage Args[MaxArgs];
|
|
|
|
template <unsigned Index>
|
|
void initArgs() {
|
|
static_assert(Index <= MaxArgs, "index out of bounds");
|
|
for (unsigned i = Index; i < MaxArgs; ++i) {
|
|
ArgKinds[i] = ArgStorageKind::None;
|
|
}
|
|
}
|
|
|
|
template <unsigned Index, class T, class... Ts>
|
|
void initArgs(T &&nextArg, Ts &&... restOfArgs) {
|
|
static_assert(Index < MaxArgs, "index out of bounds");
|
|
initArg(Index, std::forward<T>(nextArg));
|
|
initArgs<Index + 1>(std::forward<T>(restOfArgs)...);
|
|
}
|
|
|
|
void initArg(unsigned index, std::string &&string) {
|
|
ArgKinds[index] = ArgStorageKind::String;
|
|
new (&Args[index]) std::string(std::move(string));
|
|
}
|
|
|
|
void initArg(unsigned index, const std::string &string) {
|
|
ArgKinds[index] = ArgStorageKind::String;
|
|
new (&Args[index]) std::string(string);
|
|
}
|
|
|
|
void initArg(unsigned index, RemoteAddress address) {
|
|
ArgKinds[index] = ArgStorageKind::Address;
|
|
new (&Args[index]) RemoteAddress(address);
|
|
}
|
|
|
|
public:
|
|
template <class KindTy, class... Ts>
|
|
explicit Failure(KindTy kind, Ts &&...args) : TheKind(Kind(kind)) {
|
|
validateFailureArgs(kind, std::forward<Ts>(args)...);
|
|
initArgs<0>(std::forward<Ts>(args)...);
|
|
}
|
|
|
|
Failure(const Failure &other) : TheKind(other.TheKind) {
|
|
for (unsigned i = 0; i != MaxArgs; ++i) {
|
|
ArgKinds[i] = other.ArgKinds[i];
|
|
switch (ArgKinds[i]) {
|
|
case ArgStorageKind::None:
|
|
break;
|
|
case ArgStorageKind::String:
|
|
::new (&Args[i].String) std::string(other.Args[i].String);
|
|
break;
|
|
case ArgStorageKind::Address:
|
|
::new (&Args[i].Address) RemoteAddress(other.Args[i].Address);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Failure(Failure &&other) : TheKind(other.TheKind) {
|
|
for (unsigned i = 0; i != MaxArgs; ++i) {
|
|
ArgKinds[i] = other.ArgKinds[i];
|
|
switch (ArgKinds[i]) {
|
|
case ArgStorageKind::None:
|
|
break;
|
|
case ArgStorageKind::String:
|
|
::new (&Args[i].String) std::string(std::move(other.Args[i].String));
|
|
break;
|
|
case ArgStorageKind::Address:
|
|
::new (&Args[i].Address) RemoteAddress(std::move(other.Args[i].Address));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Failure &operator=(const Failure &other) {
|
|
this->~Failure();
|
|
::new (this) Failure(other);
|
|
return *this;
|
|
}
|
|
|
|
Failure &operator=(Failure &&other) {
|
|
this->~Failure();
|
|
::new (this) Failure(std::move(other));
|
|
return *this;
|
|
}
|
|
|
|
~Failure() {
|
|
for (unsigned i = 0; i != MaxArgs; ++i) {
|
|
switch (ArgKinds[i]) {
|
|
case ArgStorageKind::None:
|
|
break;
|
|
case ArgStorageKind::String:
|
|
Args[i].String.~basic_string();
|
|
break;
|
|
case ArgStorageKind::Address:
|
|
Args[i].Address.~RemoteAddress();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Return the kind of failure.
|
|
Kind getKind() const { return TheKind; }
|
|
|
|
/// Return the number of arguments to the failure.
|
|
unsigned getNumArgs() const {
|
|
unsigned i = 0;
|
|
for (; i != MaxArgs; ++i) {
|
|
// Stop at the first missing argument.
|
|
if (ArgKinds[i] == ArgStorageKind::None)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/// Render the failure as an error message.
|
|
std::string render() const {
|
|
std::string result;
|
|
|
|
const char *text = getTextForKind(TheKind);
|
|
while (const char *next = std::strchr(text, '%')) {
|
|
// Append everything we just skipped over.
|
|
result.append(text, next - text);
|
|
|
|
// Skip the '%'.
|
|
++next;
|
|
|
|
// Do something based on the character after '%'.
|
|
char c = *next++;
|
|
text = next;
|
|
|
|
if (c == '%') {
|
|
result += c;
|
|
continue;
|
|
}
|
|
|
|
assert('0' <= c && c <= '9');
|
|
unsigned argIndex = c - '0';
|
|
assert(argIndex < MaxArgs);
|
|
|
|
switch (ArgKinds[argIndex]) {
|
|
case ArgStorageKind::None:
|
|
LLVM_BUILTIN_UNREACHABLE;
|
|
|
|
// Stringize a string argument by just appending it.
|
|
case ArgStorageKind::String:
|
|
result += Args[argIndex].String;
|
|
continue;
|
|
|
|
// Stringize an address argument as a hex number. We assume that an
|
|
// address less than 2^32 is for a 32-bit target and accordingly
|
|
// only print 8 digits for it.
|
|
//
|
|
// It is really silly to provide our own hex-stringization here,
|
|
// but swift::remote is supposed to be a minimal, header-only library.
|
|
case ArgStorageKind::Address: {
|
|
result += '0';
|
|
result += 'x';
|
|
uint64_t address = Args[argIndex].Address.getRawAddress();
|
|
unsigned max = ((address >> 32) != 0 ? 16 : 8);
|
|
for (unsigned i = 0; i != max; ++i) {
|
|
result += "0123456789abcdef"[(address >> (max - 1 - i) * 4) & 0xF];
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
LLVM_BUILTIN_UNREACHABLE;
|
|
}
|
|
|
|
// Append the rest of the string.
|
|
result.append(text);
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
template <class... Tys>
|
|
struct Failure::TypeBundleForFunctionTypeParameters<void(Tys...)> {
|
|
using types = TypeBundle<Tys...>;
|
|
};
|
|
|
|
template <class... Tys> class TypeBundle {};
|
|
|
|
#define FAILURE(KIND, TEXT, ARGTYS) \
|
|
template <> struct Failure::ArgTypesForFailureKind<Failure::KIND##_t> { \
|
|
using String = ArgType_String; \
|
|
using Address = ArgType_Address; \
|
|
using types = TypeBundleForFunctionTypeParameters<void ARGTYS>::types; \
|
|
};
|
|
#include "swift/Remote/FailureKinds.def"
|
|
|
|
template <class T>
|
|
struct Failure::IsAcceptableArgType<Failure::ArgType_String, T> {
|
|
static constexpr bool value = std::is_convertible<T, std::string>::value;
|
|
};
|
|
|
|
template <>
|
|
struct Failure::IsAcceptableArgType<Failure::ArgType_Address, RemoteAddress> {
|
|
static constexpr bool value = true;
|
|
};
|
|
|
|
} // end namespace remote
|
|
} // end namespace swift
|
|
|
|
#endif // SWIFT_REMOTE_FAILURE_H
|
|
|