metadata/modules.cpp
metadata/typeprinter.cpp
protocols/cliprotocol.cpp
+ protocols/escaped_string.cpp
protocols/protocol_utils.cpp
protocols/miprotocol.cpp
protocols/tokenizer.cpp
errormessage.cpp
main.cpp
buildinfo.cpp
- utils/escaped_string.cpp
+ utils/dynlibs_unix.cpp
+ utils/dynlibs_win32.cpp
utils/filesystem.cpp
+ utils/filesystem_unix.cpp
+ utils/filesystem_win32.cpp
utils/ioredirect.cpp
+ utils/iosystem_unix.cpp
+ utils/iosystem_win32.cpp
+ utils/interop_unix.cpp
+ utils/interop_win32.cpp
+ utils/platform_unix.cpp
+ utils/platform_win32.cpp
utils/streams.cpp
- unix/interop_unix.cpp
- unix/dynlibs_unix.cpp
- unix/filesystem_unix.cpp
- unix/iosystem_unix.cpp
- unix/platform_unix.cpp
- windows/interop_win32.cpp
- windows/dynlibs_win32.cpp
- windows/filesystem_win32.cpp
- windows/iosystem_win32.cpp
- windows/platform_win32.cpp
)
set(CMAKE_INCLUDE_CURRENT_DIR OFF)
return 0;
}
+} // unnamed namespace
+
HRESULT LoadSymbolsForPortablePDB(const std::string &modulePath, BOOL isInMemory, BOOL isFileLayout, ULONG64 peAddress, ULONG64 peSize,
ULONG64 inMemoryPdbAddress, ULONG64 inMemoryPdbSize, VOID **ppSymbolReaderHandle)
{
return S_OK;
}
-} // unnamed namespace
-
-
SequencePoint::~SequencePoint() noexcept
{
Interop::SysFreeString(document);
}
-HRESULT LoadSymbols(IMetaDataImport *pMD, ICorDebugModule *pModule, VOID **ppSymbolReaderHandle)
-{
- HRESULT Status = S_OK;
- BOOL isDynamic = FALSE;
- BOOL isInMemory = FALSE;
- IfFailRet(pModule->IsDynamic(&isDynamic));
- IfFailRet(pModule->IsInMemory(&isInMemory));
-
- if (isDynamic)
- return E_FAIL; // Dynamic and in memory assemblies are a special case which we will ignore for now
-
- ULONG64 peAddress = 0;
- ULONG32 peSize = 0;
- IfFailRet(pModule->GetBaseAddress(&peAddress));
- IfFailRet(pModule->GetSize(&peSize));
-
- return LoadSymbolsForPortablePDB(
- GetModuleFileName(pModule),
- isInMemory,
- isInMemory, // isFileLayout
- peAddress,
- peSize,
- 0, // inMemoryPdbAddress
- 0, // inMemoryPdbSize
- ppSymbolReaderHandle
- );
-}
-
void DisposeSymbols(PVOID pSymbolReaderHandle)
{
std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
// WARNING! Due to CoreCLR limitations, Shutdown() can't be called out of the Main() scope, for example, from global object destructor.
void Shutdown();
- HRESULT LoadSymbols(IMetaDataImport *pMD, ICorDebugModule *pModule, VOID **ppSymbolReaderHandle);
+ HRESULT LoadSymbolsForPortablePDB(const std::string &modulePath, BOOL isInMemory, BOOL isFileLayout, ULONG64 peAddress, ULONG64 peSize,
+ ULONG64 inMemoryPdbAddress, ULONG64 inMemoryPdbSize, VOID **ppSymbolReaderHandle);
void DisposeSymbols(PVOID pSymbolReaderHandle);
HRESULT GetSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, SequencePoint *sequencePoint);
HRESULT GetNextSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, ULONG32 &ilCloseOffset, bool *noUserCodeFound);
return true;
}
+static HRESULT LoadSymbols(IMetaDataImport *pMD, ICorDebugModule *pModule, VOID **ppSymbolReaderHandle)
+{
+ HRESULT Status = S_OK;
+ BOOL isDynamic = FALSE;
+ BOOL isInMemory = FALSE;
+ IfFailRet(pModule->IsDynamic(&isDynamic));
+ IfFailRet(pModule->IsInMemory(&isInMemory));
+
+ if (isDynamic)
+ return E_FAIL; // Dynamic and in memory assemblies are a special case which we will ignore for now
+
+ ULONG64 peAddress = 0;
+ ULONG32 peSize = 0;
+ IfFailRet(pModule->GetBaseAddress(&peAddress));
+ IfFailRet(pModule->GetSize(&peSize));
+
+ return Interop::LoadSymbolsForPortablePDB(
+ GetModuleFileName(pModule),
+ isInMemory,
+ isInMemory, // isFileLayout
+ peAddress,
+ peSize,
+ 0, // inMemoryPdbAddress
+ 0, // inMemoryPdbSize
+ ppSymbolReaderHandle
+ );
+}
+
HRESULT Modules::TryLoadModuleSymbols(ICorDebugModule *pModule, Module &module, bool needJMC, std::string &outputText)
{
HRESULT Status;
module.name = GetFileName(module.path);
PVOID pSymbolReaderHandle = nullptr;
- Interop::LoadSymbols(pMDImport, pModule, &pSymbolReaderHandle);
+ LoadSymbols(pMDImport, pModule, &pSymbolReaderHandle);
module.symbolStatus = pSymbolReaderHandle != nullptr ? SymbolsLoaded : SymbolsNotFound;
if (module.symbolStatus == SymbolsLoaded)
#include <utility>
#include <type_traits>
#include <tuple>
-#include "utility.h"
#include "utils/string_view.h"
#include "utils/limits.h"
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+#include <algorithm>
+#include <string>
+#include "escaped_string.h"
+#include "assert.h"
+
+namespace netcoredbg
+{
+
+EscapedStringInternal::EscapedStringImpl::EscapedStringImpl(const EscapedStringInternal::EscapedStringImpl::Params& params, Utility::string_view str, const TempRef& ref, bool isstring)
+:
+ m_ref(&ref), m_params(params), m_input(str), m_result(), m_size(UndefinedSize), m_isstring(isstring), m_isresult(false)
+{
+ ref.set(this, &EscapedStringImpl::transform);
+}
+
+// This function performs input string transformation according to specified (via `m_params) rules.
+// Result will be passed to supplied callback function.
+void EscapedStringInternal::EscapedStringImpl::operator()(void *thiz, void (*func)(void*, Utility::string_view))
+{
+ // always have transformed result
+ if (m_isresult)
+ return func(thiz, {&m_result[0], m_result.size()});
+
+ // case, when no conversion needed
+ if (m_size == m_input.size())
+ return func(thiz, m_input);
+
+ // perform transformation and compute result size
+ size_t size = 0;
+ string_view src = m_input;
+ while (!src.empty())
+ {
+ // try to find first forbidden character
+ auto it = std::find_first_of(src.begin(), src.end(), m_params.forbidden.begin(), m_params.forbidden.end());
+ size_t prefix_size = it - src.begin();
+ if (prefix_size)
+ {
+ // output any other charactes that preceede first forbidden character
+ func(thiz, src.substr(0, prefix_size));
+ size += prefix_size;
+ }
+
+ if (it != src.end())
+ {
+ // find right substitution for forbidden character and output substituting pair of characters
+ auto ir = std::find(m_params.forbidden.begin(), m_params.forbidden.end(), *it);
+ string_view subst = m_params.subst[ir - m_params.forbidden.begin()];
+ func(thiz, subst);
+ size += subst.size();
+ prefix_size++;
+ }
+
+ src.remove_prefix(prefix_size);
+ }
+
+ // remember output size (to avoid computations in future)
+ if (m_size == UndefinedSize)
+ m_size = size;
+}
+
+// function computes output size (but not produces the output)
+size_t EscapedStringInternal::EscapedStringImpl::size() noexcept
+{
+ if (m_size == UndefinedSize)
+ (*this)(nullptr, [](void *, string_view){});
+
+ return m_size;
+}
+
+// function allocates memory and transforms string in all cases,
+// except of case, when transformation isn't required at all and
+// input arguments still not destroyed.
+EscapedStringInternal::EscapedStringImpl::operator Utility::string_view() noexcept
+{
+ if (! m_isresult)
+ {
+ if (size() == m_input.size())
+ return m_input;
+
+ transform();
+ }
+ return {&m_result[0], m_result.size()};
+}
+
+// function allocates memory and transforms string.
+EscapedStringInternal::EscapedStringImpl::operator const std::string&()
+{
+ if (!m_isresult)
+ transform();
+
+ return m_result;
+}
+
+// function allocates memory and transforms string in all cases,
+// except of case, when transformation isn't required at all, and
+// input arguments still not destroyed, and input argument contains
+// terminating zero.
+const char* EscapedStringInternal::EscapedStringImpl::c_str()
+{
+ if (m_isstring && !m_isresult && size() == m_input.size())
+ return m_input.data();
+ else
+ return static_cast<const std::string&>(*this).c_str();
+}
+
+// function performs string transformation to allocated memory
+void EscapedStringInternal::EscapedStringImpl::transform()
+{
+ m_ref->reset();
+ m_ref = nullptr;
+
+ m_result.resize(size(), 0);
+ auto it = m_result.begin();
+
+ auto func = [&](string_view str)
+ {
+ m_result.replace(it, it + str.size(), str.begin(), str.end());
+ it += str.size();
+ };
+
+ (*this)(&func, [](void *fp, string_view str) { (*static_cast<decltype(func)*>(fp))(str); });
+
+ m_isresult = true;
+ m_isstring = true;
+}
+
+} // ::netcoredbg::Utility
--- /dev/null
+// Copyright (C) 2021 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+#include <utility>
+#include <string>
+#include "utils/utility.h"
+#include "utils/span.h"
+#include "utils/string_view.h"
+
+namespace netcoredbg
+{
+
+// This namespace contains implementation details for few following classes,
+// contents of this namespace isn't designated for direct use.
+namespace EscapedStringInternal
+{
+ using Utility::string_view;
+
+ // This class allows to perform some predefined actions in moment of time,
+ // when temporary object of type TempReference is destroyed. Typically it might
+ // be used to perform some sort of lazy evaluation, when some other class accepts
+ // it's arguments and temporary TempReference object. And it's expected, that
+ // later some evaluation on arguments might be performed. But arguments might
+ // be temporary objects, which will be deleted at end of full expression.
+ // So arguments should be copied, or evaluation performed, prior deleting
+ // the arguments.
+ //
+ // When const TempReference& used as last (default) argument to
+ // class constructor, it is guaranteed, that ~TempReference will be called
+ // exactly prior to deleting other temporary arguments, so some required actions
+ // might be performed before deleting other arguments.
+ //
+ // CAUTION:
+ // When TempReference is passed to ordinary function (not a class constructor),
+ // additionaly cautions is needed: in this case construction order of function
+ // arguments isn't predefined, as destruction order (which is reverse). In such
+ // situation TempReference might be used only and only in case, when no other
+ // arguments is strictly not a temporary variables. To ensure this, you should
+ // delete functions accepting r-values: void f(const Type&&) = delete.
+ //
+ template <typename T>
+ struct TempReference
+ {
+ // This function should be called from constructor of T, and should pass
+ // reference to T's method, which be called on TempReference destruction.
+ void set(T* ptr, void (T::*func)()) const noexcept { m_ptr = ptr, m_func = func; }
+
+ // This function might be called from T to cancel callback which is set with `set`
+ // function. Especially, `reset` might be called from T's destructor.
+ void reset() const noexcept { m_ptr = nullptr; }
+
+ ~TempReference() { if (m_ptr) (m_ptr->*m_func)(); }
+
+ private:
+ mutable T* m_ptr;
+ mutable void (T::*m_func)();
+ };
+
+
+ // This is actual implementation of `EscapedString` class.
+ struct EscapedStringImpl
+ {
+ // EscapeStrinct class performs type erasure, and information on
+ // it's template parameters should be stored in `Params` class.
+ struct Params
+ {
+ string_view forbidden; // characters which must be replaced
+ Utility::span<const string_view> subst; // strings to which `forbidden` characters must be replaced
+ char escape; // character, which preceedes each substitution
+ };
+
+ using TempRef = TempReference<EscapedStringImpl>;
+
+ // `str` is the source string, in which all `forbidden` characters must be replaced,
+ // `isstring` must be set to true only in case, when `str` contains terminating zero
+ // (to which `str->end()` points).
+ EscapedStringImpl(const Params& params, string_view str, const TempRef& ref, bool isstring);
+
+ ~EscapedStringImpl() { if (m_ref) m_ref->reset(); }
+
+ // see comments in `EscapeString` class below
+ void operator()(void *thiz, void (*func)(void*, string_view));
+ size_t size() noexcept;
+ explicit operator const std::string&();
+ operator string_view() noexcept;
+ const char* c_str();
+
+ private:
+ // This function performs input string (`m_input`) transformation and stores the
+ // result in (`m_result`). This function might be called in two cases: from
+ // destructor of `TempRef` object (before deleting function argument `str`,
+ // to which `m_input` currently points), or in case when result is needed
+ // in form of data (on call of any getter function, except of operator()).
+ void transform();
+
+ static const size_t UndefinedSize = size_t(0)-1;
+
+ // This TempReference structure was passed as arument to EscapedString class
+ // constructor and continue to exist until end of full expression (till ';').
+ // At end of full expression all temporary variables destroyed and this structure too.
+ // When this happens, TempReference calls transform() method from it's destructor.
+ // In transform() method the strings, which was passed as arguments to EscapedString
+ // constructor, is transformed and copied in allocated memory, so arguments might
+ // be safely deleted. In case, if such transformation and copying occurs before
+ // calling of temporary destructors, EscapedStringImpl::transform() calls m_ref->reset()
+ // to cancel callback from TempReference class.
+ const TempReference<EscapedStringImpl>* m_ref;
+
+ const Params& m_params; // character substitution rules
+ const string_view m_input; // points to input string
+ std::string m_result; // might contain result string (lazy evaluated)
+ size_t m_size; // size of result stirng (lazy evaluated)
+ bool m_isstring; // true if m_input->end() points to terminating zero
+ bool m_isresult; // true if m_result contain result string
+ };
+} // namespace EscapedStringInternal
+
+
+/// This class allows lazy transformation of the given source string by substituting
+/// set of "forbidden" characters with escape symbol and other substituted character.
+/// The main idea is to avoid unwanted memory allocation: when it is possible no
+/// memory allocation performed. To achieve this lazy evaluation is used: string
+/// transformation is perfomed only if it is needed.
+///
+/// This class should be used in two steps: first -- creation of class instance from given
+/// arguments, at this time neither memory allocation, nor string transformation is
+/// performed. Only arguments is remembered. And second step -- calling one of the
+/// member functions (see below). At this time string transformation is performed
+/// and memory might be allocated. The latter depends on string content, which
+/// function and when was called... Memory allocation avoided in cases when no
+/// transformation is required (string doesn't contain forbidden characters) and
+/// when arguments, given to class constructor, still not destroyed. Also memory
+/// allocation always avoided in case of call to `operator()`: in this case
+/// output generated on the fly, but again only if constructor arguments still
+/// exists. String is always transformed and copy saved to allocated memory in
+/// moment, when constructor arguments destroyed (if `EscapedString` class instance
+/// is not temporary variable itself, but continue to exist after end of full expression).
+///
+template <typename Traits> class EscapedString
+{
+ using string_view = Utility::string_view;
+ using EscapedStringImpl = EscapedStringInternal::EscapedStringImpl;
+ static EscapedStringImpl::Params params;
+
+public:
+ /// Construct `EscapedString` from c-strings (via implicit conversion) or string_view.
+ /// Second parameter must have default value (shouldn't be assigned explicitly).
+ EscapedString(string_view str,
+ const EscapedStringImpl::TempRef& ref = EscapedStringImpl::TempRef())
+ :
+ impl(params, str, ref, false)
+ {}
+
+ // Note: there is no reasons to disable temporaries of type std::string, because
+ // temporary objects construction and destruction ordef for constructors are
+ // predefined, and TempRef always destructed prior to temporary string.
+
+ /// This function allows to avoid memory allocation: functor `func` given as argument
+ /// will consume parts of result (transformed string) which will be generated on the fly.
+ /// Functor `func` must accept `string_view` as argument.
+ template <typename Func, typename = decltype(std::declval<Func>()(std::declval<string_view>()))>
+ void operator()(Func&& func) const
+ {
+ impl.operator()(&func, [](void *thiz, string_view str) { (*static_cast<Func*>(thiz))(str); });
+ }
+
+ /// Function returns size of transformed string (no actual transformation performed).
+ size_t size() const noexcept { return impl.size(); }
+
+ /// Function transforms string (if it was not transformed earlier) and allocates memory.
+ explicit operator const std::string&() const& { return static_cast<const std::string&>(impl); }
+
+ /// Function transforms string and allocates memory.
+ operator string_view() const noexcept { return static_cast<string_view>(impl); }
+
+ /// Function transforms string to allocated memory.
+ const char* c_str() const { return impl.c_str(); }
+
+private:
+ mutable EscapedStringImpl impl;
+};
+
+
+// instantiation of Params structure for particular Traits template parameter
+template <typename Traits> EscapedStringInternal::EscapedStringImpl::Params EscapedString<Traits>::params =
+{
+ { ((void)([]() -> void {static_assert(sizeof(Traits::forbidden_chars)-1 == Utility::Size(Traits::subst_chars),
+ "forbidden_chars and subst_chars must have same size!");}),
+ string_view(Traits::forbidden_chars)) },
+ { Traits::subst_chars },
+ Traits::escape_char
+};
+
+
+/// Implementation of `operator<<`, which allows write `EscapedString` contents to
+/// the supplied `std::ostream` avoiding memory allocations.
+template <typename Traits>
+std::ostream& operator<<(std::ostream& os, const EscapedString<Traits>& estr)
+{
+ using string_view = Utility::string_view;
+ estr([&](string_view str) -> void { os.write(str.data(), str.size()); });
+ return os;
+}
+
+/// Overloading of `operator+` for `EscapedString`, which allows concatenation
+/// of `EscapedString` instances with ordinary strings.
+template <typename Traits, typename T>
+std::string operator+(const EscapedString<Traits>& left, T&& right)
+{
+ return static_cast<const std::string&>(left) + std::forward<T>(right);
+}
+
+/// Overloading of `operator+` for `EscapedString`, which allows concatenation
+/// of `EscapedString` instances with ordinary strings.
+template <typename Traits, typename T>
+std::string operator+(T&& left, const EscapedString<Traits>& right)
+{
+ return std::forward<T>(left) + static_cast<const std::string&>(right);
+}
+
+} // ::netcoredbg
#include <iostream>
#include <memory>
#include "utils/string_view.h"
-#include "utils/escaped_string.h"
+#include "protocols/escaped_string.h"
#include "interfaces/idebugger.h"
#include "interfaces/iprotocol.h"
#include "utils/torelease.h"
#include "utils/utf.h"
#include "utils/logger.h"
-#include "utils/escaped_string.h"
+#include "protocols/escaped_string.h"
// for convenience
using json = nlohmann::json;
# currently defined unit tests
deftest(string_view string_view_test.cpp)
deftest(span span_test.cpp)
-deftest(escaped_string ../utils/escaped_string.cpp escaped_string_test.cpp)
+deftest(escaped_string ../protocols/escaped_string.cpp escaped_string_test.cpp)
deftest(iosystem
iosystem_test.cpp
- ${PROJECT_SOURCE_DIR}/src/windows/iosystem_win32.cpp
- ${PROJECT_SOURCE_DIR}/src/unix/iosystem_unix.cpp
+ ${PROJECT_SOURCE_DIR}/src/utils/iosystem_win32.cpp
+ ${PROJECT_SOURCE_DIR}/src/utils/iosystem_unix.cpp
)
deftest(streams
streams_test.cpp
${PROJECT_SOURCE_DIR}/src/utils/streams.cpp
- ${PROJECT_SOURCE_DIR}/src/windows/iosystem_win32.cpp
- ${PROJECT_SOURCE_DIR}/src/unix/iosystem_unix.cpp
+ ${PROJECT_SOURCE_DIR}/src/utils/iosystem_win32.cpp
+ ${PROJECT_SOURCE_DIR}/src/utils/iosystem_unix.cpp
)
deftest(ioredirect
ioredirect_test.cpp
${PROJECT_SOURCE_DIR}/src/utils/ioredirect.cpp
${PROJECT_SOURCE_DIR}/src/utils/streams.cpp
- ${PROJECT_SOURCE_DIR}/src/windows/iosystem_win32.cpp
- ${PROJECT_SOURCE_DIR}/src/unix/iosystem_unix.cpp
+ ${PROJECT_SOURCE_DIR}/src/utils/iosystem_win32.cpp
+ ${PROJECT_SOURCE_DIR}/src/utils/iosystem_unix.cpp
${PROJECT_SOURCE_DIR}/src/utils/logger.cpp
)
#include <catch2/catch.hpp>
#include <string>
#include "utils/string_view.h"
-#include "utils/escaped_string.h"
+#include "protocols/escaped_string.h"
#include "compile_test.h"
using namespace netcoredbg;
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file dynlibsi_unix.h This file contains unix-specific function definitions
-/// required to work with dynamically loading libraries.
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-
-#if defined(__APPLE__)
-#include <mach-o/dyld.h>
-#include <crt_externs.h>
-#endif
-
-#include <dlfcn.h>
-#include "utils/limits.h"
-#include "utils/dynlibs.h"
-
-namespace netcoredbg
-{
-
-// This functon load specified library and returns handle (which then
-// can be passed to DLSym and DLCLose functions).
-// In case of error function returns NULL.
-DLHandle DLOpen(const std::string &path)
-{
- return reinterpret_cast<DLHandle>(::dlopen(path.c_str(), RTLD_GLOBAL | RTLD_NOW));
-}
-
-// This function resolves symbol address within library specified by handle,
-// and returns it's address, in case of error function returns NULL.
-void* DLSym(DLHandle handle, string_view name)
-{
- char str[LINE_MAX];
- if (name.size() >= sizeof(str))
- return {};
-
- name.copy(str, name.size());
- str[name.size()] = 0;
- return ::dlsym(handle, str);
-}
-
-/// This function unloads previously loadded library, specified by handle.
-/// In case of error this function returns `false'.
-bool DLClose(DLHandle handle)
-{
- return ::dlclose(handle);
-}
-
-} // ::netcoredbg
-#endif // __unix__
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file filesystem_unix.cpp
-/// This file contains definitions of unix-specific functions related to file system.
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#ifdef __APPLE__
-#include <mach-o/dyld.h>
-#endif
-#include <stdlib.h>
-#include <unistd.h>
-#include <array>
-#include <string>
-#include "utils/filesystem.h"
-#include "utils/string_view.h"
-#include "utils/span.h"
-#include "utils/limits.h"
-
-namespace netcoredbg
-{
-
-using Utility::string_view;
-template <typename T> using span = Utility::span<T>;
-
-const char* FileSystemTraits<UnixPlatformTag>::PathSeparatorSymbols = "/";
-
-namespace
-{
-#ifdef __linux__
- std::string get_exe_path()
- {
- static const char self_link[] = "/proc/self/exe";
- char buffer[PATH_MAX];
- ssize_t r = readlink(self_link, buffer, PATH_MAX);
- return std::string(buffer, r < 0 ? 0 : r);
- }
-#elif defined(__APPLE__)
- std::string get_exe_path()
- {
- uint32_t lenActualPath = 0;
- if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1)
- {
- // OSX has placed the actual path length in lenActualPath,
- // so re-attempt the operation
- std::string resizedPath(lenActualPath, '\0');
- char *pResizedPath = const_cast<char *>(resizedPath.data());
- if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0)
- return pResizedPath;
- }
- return std::string();
- }
-#endif
-}
-
-// Function returns absolute path to currently running executable.
-std::string GetExeAbsPath()
-{
- static const std::string result(get_exe_path());
- return result;
-}
-
-// Function returns path to directory, which should be used for creation of
-// temporary files. Typically this is `/tmp` on Unix and something like
-// `C:\Users\localuser\Appdata\Local\Temp` on Windows.
-string_view GetTempDir()
-{
- auto get_tmpdir = []()
- {
- const char *pPath = getenv("TMPDIR");
- if (pPath != nullptr)
- return pPath;
- else
- return P_tmpdir;
- };
-
- static const std::string result {get_tmpdir()};
- return result;
-}
-
-
-// Function changes current working directory. Return value is `false` in case of error.
-bool SetWorkDir(const std::string &path)
-{
- return chdir(path.c_str()) == 0;
-}
-
-} // ::netcoredbg
-#endif __unix__
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file filesystem_unix.h This file contains unix-specific details to FileSystem class.
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#pragma once
-#include <cstddef>
-#include "utils/limits.h"
-#include "utils/platform.h"
-
-namespace netcoredbg
-{
- template <> struct FileSystemTraits<UnixPlatformTag>
- {
- const static size_t PathMax = PATH_MAX;
- const static size_t NameMax = NAME_MAX;
- const static char PathSeparator = '/';
- const static char* PathSeparatorSymbols;
- };
-}
-#endif
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file interop_unix.h This file contains unix-specific functions for Interop class defined in interop.h
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#include <dirent.h>
-#include <stddef.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <string>
-#include <set>
-
-#include "managed/interop.h"
-#include "utils/filesystem.h"
-
-namespace netcoredbg
-{
-
-// This function searches *.dll files in specified directory and adds full paths to files
-// to colon-separated list `tpaList'.
-template <>
-void InteropTraits<UnixPlatformTag>::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList)
-{
- const char * const tpaExtensions[] = {
- ".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
- ".dll",
- ".ni.exe",
- ".exe",
- };
-
- DIR* dir = opendir(directory.c_str());
- if (dir == nullptr)
- return;
-
- std::set<std::string> addedAssemblies;
-
- // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
- // then files with .dll extension, etc.
- for (size_t extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
- {
- const char* ext = tpaExtensions[extIndex];
- int extLength = strlen(ext);
-
- struct dirent* entry;
-
- // For all entries in the directory
- while ((entry = readdir(dir)) != nullptr)
- {
- // We are interested in files only
- switch (entry->d_type)
- {
- case DT_REG:
- break;
-
- // Handle symlinks and file systems that do not support d_type
- case DT_LNK:
- case DT_UNKNOWN:
- {
- std::string fullFilename;
-
- fullFilename.append(directory);
- fullFilename += FileSystem::PathSeparator;
- fullFilename.append(entry->d_name);
-
- struct stat sb;
- if (stat(fullFilename.c_str(), &sb) == -1)
- continue;
-
- if (!S_ISREG(sb.st_mode))
- continue;
- }
- break;
-
- default:
- continue;
- }
-
- std::string filename(entry->d_name);
-
- // Check if the extension matches the one we are looking for
- int extPos = filename.length() - extLength;
- if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
- {
- continue;
- }
-
- std::string filenameWithoutExt(filename.substr(0, extPos));
-
- // Make sure if we have an assembly with multiple extensions present,
- // we insert only one version of it.
- if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
- {
- addedAssemblies.insert(filenameWithoutExt);
-
- tpaList.append(directory);
- tpaList += FileSystem::PathSeparator;
- tpaList.append(filename);
- tpaList.append(":");
- }
- }
-
- // Rewind the directory stream to be able to iterate over it for the next extension
- rewinddir(dir);
- }
-
- closedir(dir);
-}
-
-// This function unsets `CORECLR_ENABLE_PROFILING' environment variable.
-template <>
-void InteropTraits<UnixPlatformTag>::UnsetCoreCLREnv()
-{
- unsetenv("CORECLR_ENABLE_PROFILING");
-}
-
-// Returns the length of a BSTR.
-template <>
-UINT InteropTraits<UnixPlatformTag>::SysStringLen(BSTR bstrString)
-{
- if (bstrString == NULL)
- return 0;
- return (unsigned int)((((DWORD FAR*)bstrString)[-1]) / sizeof(OLECHAR));
-}
-
-} // ::netcoredbg
-#endif // __unix__
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file iosystem_unix.cpp This file contains unix-specific definitions of
-/// IOSystem class members (see iosystem.h).
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#include <cstdlib>
-#include <cassert>
-#include <cstdio>
-#include <cstring>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <stdexcept>
-#include <algorithm>
-
-#include "iosystem_unix.h"
-
-namespace
-{
- // short alias for full class name
- typedef netcoredbg::IOSystemTraits<netcoredbg::UnixPlatformTag> Class;
-
-
- struct AsyncRead
- {
- int fd;
- void* buffer;
- size_t size;
-
- AsyncRead(int fd, void *buf, size_t size) : fd(fd), buffer(buf), size(size) {}
-
- Class::IOResult operator()()
- {
- // TODO need to optimize code to left only one syscall.
- fd_set set;
- FD_ZERO(&set);
- FD_SET(fd, &set);
- struct timeval tv = {0, 0};
- ssize_t result = ::select(fd + 1, &set, NULL, &set, &tv);
- if (result == 0)
- return {Class::IOResult::Pending, 0};
-
- if (result >= 0)
- result = read(fd, buffer, size);
-
- if (result < 0)
- {
- if (errno == EAGAIN)
- return {Class::IOResult::Pending, 0};
-
- // TODO make exception class
- char msg[256];
- snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
- throw std::runtime_error(msg);
- }
-
- return {result == 0 ? Class::IOResult::Eof : Class::IOResult::Success, size_t(result)};
- }
-
- int poll(fd_set* read, fd_set *, fd_set* except) const
- {
- FD_SET(fd, read);
- FD_SET(fd, except);
- return fd;
- }
- };
-
- struct AsyncWrite
- {
- int fd;
- void const* buffer;
- size_t size;
-
- AsyncWrite(int fd, const void *buf, size_t size) : fd(fd), buffer(buf), size(size) {}
-
- Class::IOResult operator()()
- {
- fd_set set;
- FD_ZERO(&set);
- FD_SET(fd, &set);
- struct timeval tv = {0, 0};
- ssize_t result = select(fd + 1, NULL, &set, NULL, &tv);
- if (result == 0)
- return {Class::IOResult::Pending, 0};
-
- if (result >= 0)
- result = write(fd, buffer, size);
-
- if (result < 0)
- {
- if (errno == EAGAIN)
- return {Class::IOResult::Pending, 0};
-
- char msg[256];
- snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
- throw std::runtime_error(msg);
- }
-
- return {Class::IOResult::Success, size_t(result)};
- }
-
-
- int poll(fd_set *, fd_set *write, fd_set *) const
- {
- FD_SET(fd, write);
- return fd;
- }
- };
-}
-
-
-template <typename T> Class::AsyncHandle::Traits Class::AsyncHandle::TraitsImpl<T>::traits =
-{
- [](void *thiz)
- -> Class::IOResult { return reinterpret_cast<T*>(thiz)->operator()(); },
-
- [](void *thiz, fd_set* read, fd_set* write, fd_set* except)
- -> int { return reinterpret_cast<T*>(thiz)->poll(read, write, except); },
-
- [](void *src, void *dst)
- -> void { *reinterpret_cast<T*>(dst) = *reinterpret_cast<T*>(src); },
-
- [](void *thiz)
- -> void { reinterpret_cast<T*>(thiz)->~T(); }
-};
-
-
-// Function should create unnamed pipe and return two file handles
-// (reading and writing pipe ends) or return empty file handles if pipe can't be created.
-std::pair<Class::FileHandle, Class::FileHandle> Class::unnamed_pipe()
-{
- int fds[2];
- if (::pipe(fds) < 0)
- {
- perror("pipe");
- return {};
- }
-
- // TODO what to do with this?
- signal(SIGPIPE, SIG_IGN);
-
- return { fds[0], fds[1] };
-}
-
-
-// Function creates listening TCP socket on given port, waits, accepts single
-// connection, and return file descriptor related to the accepted connection.
-// In case of error, empty file handle will be returned.
-Class::FileHandle Class::listen_socket(unsigned port)
-{
- assert(port > 0 && port < 65536);
-
- int newsockfd;
- socklen_t clilen;
- struct sockaddr_in serv_addr, cli_addr;
-
- int sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
- if (sockFd < 0)
- {
- perror("can't create socket");
- return {};
- }
-
- int enable = 1;
- if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
- {
- ::close(sockFd);
- perror("can't set socket options");
- return {};
- }
- memset(&serv_addr, 0, sizeof(serv_addr));
-
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_addr.s_addr = INADDR_ANY;
- serv_addr.sin_port = htons(port);
-
- if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
- {
- ::close(sockFd);
- perror("can't bind to specified port");
- return {};
- }
-
- ::listen(sockFd, 1);
-
-#ifdef DEBUGGER_FOR_TIZEN
- // On Tizen, launch_app won't terminate until stdin, stdout and stderr are closed.
- // But Visual Studio initiates the connection only after launch_app termination,
- // therefore we need to close the descriptors before the call to accept().
- int fd_null = open("/dev/null", O_WRONLY | O_APPEND);
- if (fd_null < 0)
- {
- ::close(sockFd);
- perror("can't open /dev/null");
- return {};
- }
-
- // Silently close previous stdin/stdout/stderr and reuse fds.
- // Don't allow stdin read (EOF), but allow stdout/stderr write.
- if (dup2(fd_null, STDIN_FILENO) == -1 ||
- dup2(fd_null, STDOUT_FILENO) == -1 ||
- dup2(fd_null, STDERR_FILENO) == -1)
- {
- ::close(sockFd);
- perror("can't dup2");
- return {};
- }
-
- close(fd_null);
-
- //TODO on Tizen redirect stderr/stdout output into dlog
-#endif
-
- clilen = sizeof(cli_addr);
- newsockfd = ::accept(sockFd, (struct sockaddr *) &cli_addr, &clilen);
- ::close(sockFd);
- if (newsockfd < 0)
- {
- perror("accept");
- return {};
- }
-
- return newsockfd;
-}
-
-// Enable/disable handle inheritance for child processes.
-Class::IOResult Class::set_inherit(const FileHandle &fh, bool inherit)
-{
- int flags = fcntl(fh.fd, F_GETFD);
- if (flags < 0)
- return {IOResult::Error, 0};
-
- if (inherit)
- flags &= ~FD_CLOEXEC;
- else
- flags |= FD_CLOEXEC;
-
- if (fcntl(fh.fd, F_SETFD, flags) < 0)
- return {IOResult::Error, 0};
-
- return {IOResult::Success, 0};
-}
-
-// Function perform reading from the file: it may read up to `count' bytes to `buf'.
-Class::IOResult Class::read(const FileHandle &fh, void *buf, size_t count)
-{
- ssize_t rsize = ::read(fh.fd, buf, count);
- if (rsize < 0)
- return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 };
- else
- return { (rsize == 0 ? IOResult::Eof : IOResult::Success), size_t(rsize) };
-}
-
-
-// Function perform writing to the file: it may write up to `count' byte from `buf'.
-Class::IOResult Class::write(const FileHandle &fh, const void *buf, size_t count)
-{
- ssize_t wsize = ::write(fh.fd, buf, count);
- if (wsize < 0)
- return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 };
- else
- return { IOResult::Success, size_t(wsize) };
-}
-
-
-Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count)
-{
- return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create<AsyncRead>(fh.fd, buf, count);
-}
-
-Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count)
-{
- return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create<AsyncWrite>(fh.fd, buf, count);
-}
-
-
-bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout)
-{
- fd_set read_set, write_set, except_set;
- FD_ZERO(&read_set);
- FD_ZERO(&write_set);
- FD_ZERO(&except_set);
-
- int maxfd = -1;
- for (IOSystem::AsyncHandleIterator it = begin; it != end; ++it)
- {
- if (*it)
- maxfd = std::max(it->handle.poll(&read_set, &write_set, &except_set), maxfd);
- }
-
- struct timeval tv;
- std::chrono::microseconds us = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
- tv.tv_sec = us.count() / 1000000, tv.tv_usec = us.count() % 1000000;
-
- int result;
- do result = ::select(maxfd + 1, &read_set, &write_set, &except_set, &tv);
- while (result < 0 && errno == EINTR);
-
- if (result < 0)
- {
- char msg[256];
- snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
- throw std::runtime_error(msg);
- }
-
- return result > 0;
-}
-
-Class::IOResult Class::async_cancel(Class::AsyncHandle& handle)
-{
- if (!handle)
- return {Class::IOResult::Error, 0};
-
- handle = {};
- return {Class::IOResult::Success, 0};
-}
-
-Class::IOResult Class::async_result(Class::AsyncHandle& handle)
-{
- if (!handle)
- return {Class::IOResult::Error, 0};
-
- auto result = handle();
- if (result.status != Class::IOResult::Pending)
- handle = {};
-
- return result;
-}
-
-
-// Function closes the file represented by file handle.
-Class::IOResult Class::close(const FileHandle &fh)
-{
- return { (::close(fh.fd) == 0 ? IOResult::Success : IOResult::Error), 0 };
-}
-
-
-// This function returns triplet of currently selected standard files.
-netcoredbg::IOSystem::StdFiles Class::get_std_files()
-{
- static const IOSystem::FileHandle handles[std::tuple_size<IOSystem::StdFiles>::value] = {
- FileHandle(STDIN_FILENO), FileHandle(STDOUT_FILENO), FileHandle(STDERR_FILENO)
- };
- return {handles[0], handles[1], handles[2]};
-}
-
-
-// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor.
-// Substitution exists only during life time of StsIOSwap instance.
-Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true)
-{
- const static unsigned NFD = std::tuple_size<StdFiles>::value;
- static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr };
-
- const int newfd[NFD] = {
- std::get<StdFileType::Stdin>(files).handle.fd,
- std::get<StdFileType::Stdout>(files).handle.fd,
- std::get<StdFileType::Stderr>(files).handle.fd };
-
- for (unsigned n = 0; n < NFD; n++)
- {
- m_orig_fd[n] = ::dup(oldfd[n]);
- if (m_orig_fd[n] == -1)
- {
- char msg[256];
- snprintf(msg, sizeof(msg), "dup(%d): %s", oldfd[n], strerror(errno));
- throw std::runtime_error(msg);
- }
-
- if (::dup2(newfd[n], oldfd[n]) == -1)
- {
- char msg[256];
- snprintf(msg, sizeof(msg), "dup2(%d, %d): %s", newfd[n], oldfd[n], strerror(errno));
- throw std::runtime_error(msg);
- }
- }
-}
-
-
-Class::StdIOSwap::~StdIOSwap()
-{
- if (!m_valid)
- return;
-
- const static unsigned NFD = std::tuple_size<StdFiles>::value;
- static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr };
- for (unsigned n = 0; n < NFD; n++)
- {
- if (::dup2(m_orig_fd[n], oldfd[n]) == -1)
- {
- abort();
- }
-
- ::close(m_orig_fd[n]);
- }
-}
-
-#endif // __unix__
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file iosystem_unix.h This file contains unix-specific declaration of IOSystem class (see iosystem.h).
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#pragma once
-#include <cstdlib>
-#include <cassert>
-#include <sys/select.h>
-#include <tuple>
-#include <new>
-
-#include "utils/platform.h"
-#include "utils/iosystem.h"
-
-template <> struct netcoredbg::IOSystemTraits<netcoredbg::UnixPlatformTag>
-{
- using IOSystem = typename netcoredbg::IOSystemImpl<IOSystemTraits<UnixPlatformTag> >;
- using IOResult = IOSystem::IOResult;
-
- struct FileHandle
- {
- FileHandle() : fd(-1) {}
- FileHandle(int n) : fd(n) {}
- explicit operator bool() const { return fd != -1; }
-
- int fd;
- };
-
- struct AsyncHandle
- {
- struct Traits
- {
- IOResult (*oper)(void *thiz);
- int (*poll)(void *thiz, fd_set *, fd_set *, fd_set *);
- void (*move)(void* src, void *dst);
- void (*destr)(void *thiz);
- };
-
- template <typename T> struct TraitsImpl
- {
- static struct Traits traits;
- };
-
- const Traits *traits;
- mutable char data alignas(__BIGGEST_ALIGNMENT__) [sizeof(void*) * 4];
-
- explicit operator bool() const { return !!traits; }
-
- IOResult operator()() { assert(*this); return traits->oper(data); }
-
- int poll(fd_set* read, fd_set* write, fd_set* except)
- {
- assert(*this);
- return traits->poll(data, read, write, except);
- }
-
- AsyncHandle() : traits(nullptr) {}
-
- template <typename InstanceType, typename... Args>
- static AsyncHandle create(Args&&... args)
- {
- static_assert(sizeof(InstanceType) <= sizeof(data), "insufficiend data size");
- AsyncHandle result;
- result.traits = &TraitsImpl<InstanceType>::traits;
- new (result.data) InstanceType(std::forward<Args>(args)...);
- return result;
- }
-
- AsyncHandle(AsyncHandle&& other) : traits(other.traits)
- {
- if (other) traits->move(other.data, data);
- other.traits = nullptr;
- }
-
- AsyncHandle& operator=(AsyncHandle&& other)
- {
- this->~AsyncHandle();
- return *new (this) AsyncHandle(std::move(other));
- }
-
- ~AsyncHandle() { if (*this) traits->destr(data); }
- };
-
- static std::pair<FileHandle, FileHandle> unnamed_pipe();
- static FileHandle listen_socket(unsigned tcp_port);
- static IOResult set_inherit(const FileHandle&, bool);
- static IOResult read(const FileHandle&, void *buf, size_t count);
- static IOResult write(const FileHandle&, const void *buf, size_t count);
- static AsyncHandle async_read(const FileHandle&, void *buf, size_t count);
- static AsyncHandle async_write(const FileHandle&, const void *buf, size_t count);
- static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds);
- static IOResult async_cancel(AsyncHandle&);
- static IOResult async_result(AsyncHandle&);
- static IOResult close(const FileHandle&);
-
- struct StdIOSwap
- {
- using StdFiles = IOSystem::StdFiles;
- using StdFileType = IOSystem::StdFileType;
- StdIOSwap(const StdFiles &);
- ~StdIOSwap();
-
- StdIOSwap(StdIOSwap&& other)
- {
- m_valid = other.m_valid;
- if (!m_valid)
- return;
-
- other.m_valid = false;
- for (unsigned n = 0; n < std::tuple_size<StdFiles>::value; n++)
- m_orig_fd[n] = other.m_orig_fd[n];
- }
-
- bool m_valid;
- int m_orig_fd[std::tuple_size<StdFiles>::value];
- };
-
- static IOSystem::StdFiles get_std_files();
-};
-
-#endif // __unix__
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file platform_unix.cpp This file contains unix-specific function definitions,
-/// for functions defined in platform.h
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#ifdef __APPLE__
-#include <crt_externs.h>
-#endif
-#include <unistd.h>
-#include "utils/platform.h"
-
-extern char** environ;
-
-namespace netcoredbg
-{
-
-// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix).
-unsigned long OSPageSize()
-{
- static unsigned long pageSize = sysconf(_SC_PAGESIZE);
- return pageSize;
-}
-
-
-// Function suspends process execution for specified amount of time (in microseconds)
-void USleep(unsigned long usec)
-{
- usleep(usec);
-}
-
-
-// Function returns list of environment variables (like char **environ).
-char** GetSystemEnvironment()
-{
-#if __APPLE__
- return *(_NSGetEnviron());
-#else // __APPLE__
- return environ;
-#endif // __APPLE__
-}
-
-} // ::netcoredbg
-#endif // __unix__
+++ /dev/null
-// Copyright (c) 2020 Samsung Electronics Co., LTD
-// Distributed under the MIT License.
-// See the LICENSE file in the project root for more information.
-
-// \file utility.h This file contains few supplimentar classes and functions residing in Utility namespace.
-
-#pragma once
-#include <stddef.h>
-
-namespace netcoredbg
-{
-
-namespace Utility
-{
-
-/// @{ This function is similar to `std::size()` from C++17 and allows to determine
-/// the object size as number of elements, which might be stored within object
-/// (opposed to sizeof(), which returns object sizes in bytes). Typically these
-/// functions applicable to arrays and to classes like std::array.
-template <typename T> constexpr auto Size(const T& v) -> decltype(v.size()) { return v.size(); }
-template <class T, size_t N> constexpr size_t Size(const T (&)[N]) noexcept { return N; }
-/// @}
-
-
-/// This type is similar to `std::index_sequence` from C++14 and typically
-/// should be used in pair with `MakeSequence` type to create sequence of
-/// indices usable in template metaprogramming techniques.
-template <size_t... Index> struct Sequence {};
-
-// Actual implementation of MakeSequence type.
-namespace Internals
-{
- template <size_t Size, size_t... Index> struct MakeSequence : MakeSequence<Size-1, Size-1, Index...> {};
- template <size_t... Index> struct MakeSequence<0, Index...> { typedef Sequence<Index...> type; };
-}
-
-/// MakeSequence type is similar to `std::make_index_sequence<N>` from C++14.
-/// Instantination of this type exapands to `Sequence<size_t...N>` type, where
-/// `N` have values from 0 to `Size-1`.
-template <size_t Size> using MakeSequence = typename Internals::MakeSequence<Size>::type;
-
-
-/// @{ This is similar to std:void_t which is defined in c++17.
-template <typename... Args> struct MakeVoid { typedef void type; };
-template <typename... Args> using Void = typename MakeVoid<Args...>::type;
-/// @}
-
-
-/// This template is similar to `offsetof` macros in plain C. It allows
-/// to get offset of specified member in some particular class.
-template <typename Owner, typename Member>
-static inline constexpr size_t offset_of(const Member Owner::*mem)
-{
- return reinterpret_cast<size_t>(&(reinterpret_cast<Owner*>(0)->*mem));
-}
-
-/// This template is similar to well known `container_of` macros. It allows
-/// to get pointer to owner class from pointer to member.
-template <typename Owner, typename Member>
-static inline constexpr Owner *container_of(Member *ptr, const Member Owner::*mem)
-{
- return reinterpret_cast<Owner*>(reinterpret_cast<size_t>(ptr) - offset_of(mem));
-}
-
-
-// This is helper class which simplifies implementation of singleton classes.
-//
-// Usage example:
-// 1) define dictinct type of singleton: typedef Singleton<YourType> YourSingleton;
-// 2) to access your singleton use expression: YourSingleton::instance().operations...
-//
-template <typename T> struct Singleton
-{
- static T& instance()
- {
- static T val;
- return val;
- }
-};
-
-} // Utility namespace
-} // namespace netcoredbg
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file dynlibsi_unix.h This file contains unix-specific function definitions
+/// required to work with dynamically loading libraries.
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#include <crt_externs.h>
+#endif
+
+#include <dlfcn.h>
+#include "utils/limits.h"
+#include "utils/dynlibs.h"
+
+namespace netcoredbg
+{
+
+// This functon load specified library and returns handle (which then
+// can be passed to DLSym and DLCLose functions).
+// In case of error function returns NULL.
+DLHandle DLOpen(const std::string &path)
+{
+ return reinterpret_cast<DLHandle>(::dlopen(path.c_str(), RTLD_GLOBAL | RTLD_NOW));
+}
+
+// This function resolves symbol address within library specified by handle,
+// and returns it's address, in case of error function returns NULL.
+void* DLSym(DLHandle handle, string_view name)
+{
+ char str[LINE_MAX];
+ if (name.size() >= sizeof(str))
+ return {};
+
+ name.copy(str, name.size());
+ str[name.size()] = 0;
+ return ::dlsym(handle, str);
+}
+
+/// This function unloads previously loadded library, specified by handle.
+/// In case of error this function returns `false'.
+bool DLClose(DLHandle handle)
+{
+ return ::dlclose(handle);
+}
+
+} // ::netcoredbg
+#endif // __unix__
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file dynlibsi_win32.h This file contains windows-specific function definitions
+/// required to work with dynamically loading libraries.
+
+#ifdef WIN32
+#include <windows.h>
+#include "utils/dynlibs.h"
+#include "utils/limits.h"
+
+namespace netcoredbg
+{
+
+// This functon load specified library and returns handle (which then
+// can be passed to DLSym and DLCLose functions).
+// In case of error function returns NULL.
+DLHandle DLOpen(const std::string &path)
+{
+ return reinterpret_cast<DLHandle>(::LoadLibraryExA(path.c_str(), NULL, 0));
+}
+
+// This function resolves symbol address within library specified by handle,
+// and returns it's address, in case of error function returns NULL.
+void* DLSym(DLHandle handle, string_view name)
+{
+ char str[LINE_MAX];
+ if (name.size() >= sizeof(str))
+ return {};
+
+ name.copy(str, name.size());
+ str[name.size()] = 0;
+ return ::GetProcAddress((HMODULE)handle, str);
+}
+
+/// This function unloads previously loadded library, specified by handle.
+/// In case of error this function returns `false'.
+bool DLClose(DLHandle handle)
+{
+ return ::FreeLibrary(reinterpret_cast<HMODULE>(handle));
+}
+
+} // ::netcoredbg
+#endif
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-#include <algorithm>
-#include <string>
-#include "escaped_string.h"
-#include "assert.h"
-
-namespace netcoredbg
-{
-
-EscapedStringInternal::EscapedStringImpl::EscapedStringImpl(const EscapedStringInternal::EscapedStringImpl::Params& params, Utility::string_view str, const TempRef& ref, bool isstring)
-:
- m_ref(&ref), m_params(params), m_input(str), m_result(), m_size(UndefinedSize), m_isstring(isstring), m_isresult(false)
-{
- ref.set(this, &EscapedStringImpl::transform);
-}
-
-// This function performs input string transformation according to specified (via `m_params) rules.
-// Result will be passed to supplied callback function.
-void EscapedStringInternal::EscapedStringImpl::operator()(void *thiz, void (*func)(void*, Utility::string_view))
-{
- // always have transformed result
- if (m_isresult)
- return func(thiz, {&m_result[0], m_result.size()});
-
- // case, when no conversion needed
- if (m_size == m_input.size())
- return func(thiz, m_input);
-
- // perform transformation and compute result size
- size_t size = 0;
- string_view src = m_input;
- while (!src.empty())
- {
- // try to find first forbidden character
- auto it = std::find_first_of(src.begin(), src.end(), m_params.forbidden.begin(), m_params.forbidden.end());
- size_t prefix_size = it - src.begin();
- if (prefix_size)
- {
- // output any other charactes that preceede first forbidden character
- func(thiz, src.substr(0, prefix_size));
- size += prefix_size;
- }
-
- if (it != src.end())
- {
- // find right substitution for forbidden character and output substituting pair of characters
- auto ir = std::find(m_params.forbidden.begin(), m_params.forbidden.end(), *it);
- string_view subst = m_params.subst[ir - m_params.forbidden.begin()];
- func(thiz, subst);
- size += subst.size();
- prefix_size++;
- }
-
- src.remove_prefix(prefix_size);
- }
-
- // remember output size (to avoid computations in future)
- if (m_size == UndefinedSize)
- m_size = size;
-}
-
-// function computes output size (but not produces the output)
-size_t EscapedStringInternal::EscapedStringImpl::size() noexcept
-{
- if (m_size == UndefinedSize)
- (*this)(nullptr, [](void *, string_view){});
-
- return m_size;
-}
-
-// function allocates memory and transforms string in all cases,
-// except of case, when transformation isn't required at all and
-// input arguments still not destroyed.
-EscapedStringInternal::EscapedStringImpl::operator Utility::string_view() noexcept
-{
- if (! m_isresult)
- {
- if (size() == m_input.size())
- return m_input;
-
- transform();
- }
- return {&m_result[0], m_result.size()};
-}
-
-// function allocates memory and transforms string.
-EscapedStringInternal::EscapedStringImpl::operator const std::string&()
-{
- if (!m_isresult)
- transform();
-
- return m_result;
-}
-
-// function allocates memory and transforms string in all cases,
-// except of case, when transformation isn't required at all, and
-// input arguments still not destroyed, and input argument contains
-// terminating zero.
-const char* EscapedStringInternal::EscapedStringImpl::c_str()
-{
- if (m_isstring && !m_isresult && size() == m_input.size())
- return m_input.data();
- else
- return static_cast<const std::string&>(*this).c_str();
-}
-
-// function performs string transformation to allocated memory
-void EscapedStringInternal::EscapedStringImpl::transform()
-{
- m_ref->reset();
- m_ref = nullptr;
-
- m_result.resize(size(), 0);
- auto it = m_result.begin();
-
- auto func = [&](string_view str)
- {
- m_result.replace(it, it + str.size(), str.begin(), str.end());
- it += str.size();
- };
-
- (*this)(&func, [](void *fp, string_view str) { (*static_cast<decltype(func)*>(fp))(str); });
-
- m_isresult = true;
- m_isstring = true;
-}
-
-} // ::netcoredbg::Utility
+++ /dev/null
-// Copyright (C) 2021 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-#pragma once
-#include <utility>
-#include <string>
-#include "utility.h"
-#include "utils/span.h"
-#include "utils/string_view.h"
-
-namespace netcoredbg
-{
-
-// This namespace contains implementation details for few following classes,
-// contents of this namespace isn't designated for direct use.
-namespace EscapedStringInternal
-{
- using Utility::string_view;
-
- // This class allows to perform some predefined actions in moment of time,
- // when temporary object of type TempReference is destroyed. Typically it might
- // be used to perform some sort of lazy evaluation, when some other class accepts
- // it's arguments and temporary TempReference object. And it's expected, that
- // later some evaluation on arguments might be performed. But arguments might
- // be temporary objects, which will be deleted at end of full expression.
- // So arguments should be copied, or evaluation performed, prior deleting
- // the arguments.
- //
- // When const TempReference& used as last (default) argument to
- // class constructor, it is guaranteed, that ~TempReference will be called
- // exactly prior to deleting other temporary arguments, so some required actions
- // might be performed before deleting other arguments.
- //
- // CAUTION:
- // When TempReference is passed to ordinary function (not a class constructor),
- // additionaly cautions is needed: in this case construction order of function
- // arguments isn't predefined, as destruction order (which is reverse). In such
- // situation TempReference might be used only and only in case, when no other
- // arguments is strictly not a temporary variables. To ensure this, you should
- // delete functions accepting r-values: void f(const Type&&) = delete.
- //
- template <typename T>
- struct TempReference
- {
- // This function should be called from constructor of T, and should pass
- // reference to T's method, which be called on TempReference destruction.
- void set(T* ptr, void (T::*func)()) const noexcept { m_ptr = ptr, m_func = func; }
-
- // This function might be called from T to cancel callback which is set with `set`
- // function. Especially, `reset` might be called from T's destructor.
- void reset() const noexcept { m_ptr = nullptr; }
-
- ~TempReference() { if (m_ptr) (m_ptr->*m_func)(); }
-
- private:
- mutable T* m_ptr;
- mutable void (T::*m_func)();
- };
-
-
- // This is actual implementation of `EscapedString` class.
- struct EscapedStringImpl
- {
- // EscapeStrinct class performs type erasure, and information on
- // it's template parameters should be stored in `Params` class.
- struct Params
- {
- string_view forbidden; // characters which must be replaced
- Utility::span<const string_view> subst; // strings to which `forbidden` characters must be replaced
- char escape; // character, which preceedes each substitution
- };
-
- using TempRef = TempReference<EscapedStringImpl>;
-
- // `str` is the source string, in which all `forbidden` characters must be replaced,
- // `isstring` must be set to true only in case, when `str` contains terminating zero
- // (to which `str->end()` points).
- EscapedStringImpl(const Params& params, string_view str, const TempRef& ref, bool isstring);
-
- ~EscapedStringImpl() { if (m_ref) m_ref->reset(); }
-
- // see comments in `EscapeString` class below
- void operator()(void *thiz, void (*func)(void*, string_view));
- size_t size() noexcept;
- explicit operator const std::string&();
- operator string_view() noexcept;
- const char* c_str();
-
- private:
- // This function performs input string (`m_input`) transformation and stores the
- // result in (`m_result`). This function might be called in two cases: from
- // destructor of `TempRef` object (before deleting function argument `str`,
- // to which `m_input` currently points), or in case when result is needed
- // in form of data (on call of any getter function, except of operator()).
- void transform();
-
- static const size_t UndefinedSize = size_t(0)-1;
-
- // This TempReference structure was passed as arument to EscapedString class
- // constructor and continue to exist until end of full expression (till ';').
- // At end of full expression all temporary variables destroyed and this structure too.
- // When this happens, TempReference calls transform() method from it's destructor.
- // In transform() method the strings, which was passed as arguments to EscapedString
- // constructor, is transformed and copied in allocated memory, so arguments might
- // be safely deleted. In case, if such transformation and copying occurs before
- // calling of temporary destructors, EscapedStringImpl::transform() calls m_ref->reset()
- // to cancel callback from TempReference class.
- const TempReference<EscapedStringImpl>* m_ref;
-
- const Params& m_params; // character substitution rules
- const string_view m_input; // points to input string
- std::string m_result; // might contain result string (lazy evaluated)
- size_t m_size; // size of result stirng (lazy evaluated)
- bool m_isstring; // true if m_input->end() points to terminating zero
- bool m_isresult; // true if m_result contain result string
- };
-} // namespace EscapedStringInternal
-
-
-/// This class allows lazy transformation of the given source string by substituting
-/// set of "forbidden" characters with escape symbol and other substituted character.
-/// The main idea is to avoid unwanted memory allocation: when it is possible no
-/// memory allocation performed. To achieve this lazy evaluation is used: string
-/// transformation is perfomed only if it is needed.
-///
-/// This class should be used in two steps: first -- creation of class instance from given
-/// arguments, at this time neither memory allocation, nor string transformation is
-/// performed. Only arguments is remembered. And second step -- calling one of the
-/// member functions (see below). At this time string transformation is performed
-/// and memory might be allocated. The latter depends on string content, which
-/// function and when was called... Memory allocation avoided in cases when no
-/// transformation is required (string doesn't contain forbidden characters) and
-/// when arguments, given to class constructor, still not destroyed. Also memory
-/// allocation always avoided in case of call to `operator()`: in this case
-/// output generated on the fly, but again only if constructor arguments still
-/// exists. String is always transformed and copy saved to allocated memory in
-/// moment, when constructor arguments destroyed (if `EscapedString` class instance
-/// is not temporary variable itself, but continue to exist after end of full expression).
-///
-template <typename Traits> class EscapedString
-{
- using string_view = Utility::string_view;
- using EscapedStringImpl = EscapedStringInternal::EscapedStringImpl;
- static EscapedStringImpl::Params params;
-
-public:
- /// Construct `EscapedString` from c-strings (via implicit conversion) or string_view.
- /// Second parameter must have default value (shouldn't be assigned explicitly).
- EscapedString(string_view str,
- const EscapedStringImpl::TempRef& ref = EscapedStringImpl::TempRef())
- :
- impl(params, str, ref, false)
- {}
-
- // Note: there is no reasons to disable temporaries of type std::string, because
- // temporary objects construction and destruction ordef for constructors are
- // predefined, and TempRef always destructed prior to temporary string.
-
- /// This function allows to avoid memory allocation: functor `func` given as argument
- /// will consume parts of result (transformed string) which will be generated on the fly.
- /// Functor `func` must accept `string_view` as argument.
- template <typename Func, typename = decltype(std::declval<Func>()(std::declval<string_view>()))>
- void operator()(Func&& func) const
- {
- impl.operator()(&func, [](void *thiz, string_view str) { (*static_cast<Func*>(thiz))(str); });
- }
-
- /// Function returns size of transformed string (no actual transformation performed).
- size_t size() const noexcept { return impl.size(); }
-
- /// Function transforms string (if it was not transformed earlier) and allocates memory.
- explicit operator const std::string&() const& { return static_cast<const std::string&>(impl); }
-
- /// Function transforms string and allocates memory.
- operator string_view() const noexcept { return static_cast<string_view>(impl); }
-
- /// Function transforms string to allocated memory.
- const char* c_str() const { return impl.c_str(); }
-
-private:
- mutable EscapedStringImpl impl;
-};
-
-
-// instantiation of Params structure for particular Traits template parameter
-template <typename Traits> EscapedStringInternal::EscapedStringImpl::Params EscapedString<Traits>::params =
-{
- { ((void)([]() -> void {static_assert(sizeof(Traits::forbidden_chars)-1 == Utility::Size(Traits::subst_chars),
- "forbidden_chars and subst_chars must have same size!");}),
- string_view(Traits::forbidden_chars)) },
- { Traits::subst_chars },
- Traits::escape_char
-};
-
-
-/// Implementation of `operator<<`, which allows write `EscapedString` contents to
-/// the supplied `std::ostream` avoiding memory allocations.
-template <typename Traits>
-std::ostream& operator<<(std::ostream& os, const EscapedString<Traits>& estr)
-{
- using string_view = Utility::string_view;
- estr([&](string_view str) -> void { os.write(str.data(), str.size()); });
- return os;
-}
-
-/// Overloading of `operator+` for `EscapedString`, which allows concatenation
-/// of `EscapedString` instances with ordinary strings.
-template <typename Traits, typename T>
-std::string operator+(const EscapedString<Traits>& left, T&& right)
-{
- return static_cast<const std::string&>(left) + std::forward<T>(right);
-}
-
-/// Overloading of `operator+` for `EscapedString`, which allows concatenation
-/// of `EscapedString` instances with ordinary strings.
-template <typename Traits, typename T>
-std::string operator+(T&& left, const EscapedString<Traits>& right)
-{
- return std::forward<T>(left) + static_cast<const std::string&>(right);
-}
-
-} // ::netcoredbg
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file filesystem_unix.cpp
+/// This file contains definitions of unix-specific functions related to file system.
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+#include <array>
+#include <string>
+#include "utils/filesystem.h"
+#include "utils/string_view.h"
+#include "utils/span.h"
+#include "utils/limits.h"
+
+namespace netcoredbg
+{
+
+using Utility::string_view;
+template <typename T> using span = Utility::span<T>;
+
+const char* FileSystemTraits<UnixPlatformTag>::PathSeparatorSymbols = "/";
+
+namespace
+{
+#ifdef __linux__
+ std::string get_exe_path()
+ {
+ static const char self_link[] = "/proc/self/exe";
+ char buffer[PATH_MAX];
+ ssize_t r = readlink(self_link, buffer, PATH_MAX);
+ return std::string(buffer, r < 0 ? 0 : r);
+ }
+#elif defined(__APPLE__)
+ std::string get_exe_path()
+ {
+ uint32_t lenActualPath = 0;
+ if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1)
+ {
+ // OSX has placed the actual path length in lenActualPath,
+ // so re-attempt the operation
+ std::string resizedPath(lenActualPath, '\0');
+ char *pResizedPath = const_cast<char *>(resizedPath.data());
+ if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0)
+ return pResizedPath;
+ }
+ return std::string();
+ }
+#endif
+}
+
+// Function returns absolute path to currently running executable.
+std::string GetExeAbsPath()
+{
+ static const std::string result(get_exe_path());
+ return result;
+}
+
+// Function returns path to directory, which should be used for creation of
+// temporary files. Typically this is `/tmp` on Unix and something like
+// `C:\Users\localuser\Appdata\Local\Temp` on Windows.
+string_view GetTempDir()
+{
+ auto get_tmpdir = []()
+ {
+ const char *pPath = getenv("TMPDIR");
+ if (pPath != nullptr)
+ return pPath;
+ else
+ return P_tmpdir;
+ };
+
+ static const std::string result {get_tmpdir()};
+ return result;
+}
+
+
+// Function changes current working directory. Return value is `false` in case of error.
+bool SetWorkDir(const std::string &path)
+{
+ return chdir(path.c_str()) == 0;
+}
+
+} // ::netcoredbg
+#endif __unix__
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file filesystem_unix.h This file contains unix-specific details to FileSystem class.
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#pragma once
+#include <cstddef>
+#include "utils/limits.h"
+#include "utils/platform.h"
+
+namespace netcoredbg
+{
+ template <> struct FileSystemTraits<UnixPlatformTag>
+ {
+ const static size_t PathMax = PATH_MAX;
+ const static size_t NameMax = NAME_MAX;
+ const static char PathSeparator = '/';
+ const static char* PathSeparatorSymbols;
+ };
+}
+#endif
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file filesystem_win32.cpp
+/// This file contains definitions of windows-specific functions related to file system.
+
+#ifdef WIN32
+#include <windows.h>
+#include <string>
+#include "utils/filesystem.h"
+#include "utils/limits.h"
+
+
+namespace netcoredbg
+{
+
+const char* FileSystemTraits<Win32PlatformTag>::PathSeparatorSymbols = "/\\";
+
+// Function returns absolute path to currently running executable.
+std::string GetExeAbsPath()
+{
+ const size_t MAX_LONGPATH = 1024;
+ char hostPath[MAX_LONGPATH + 1];
+ static const std::string result(hostPath, ::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH));
+ return result;
+}
+
+
+// Function returns path to directory, which should be used for creation of
+// temporary files. Typically this is `/tmp` on Unix and something like
+// `C:\Users\localuser\Appdata\Local\Temp` on Windows.
+string_view GetTempDir()
+{
+ CHAR path[MAX_PATH + 1];
+ static const std::string result(path, GetTempPathA(MAX_PATH, path));
+ return result;
+}
+
+
+// Function changes current working directory. Return value is `false` in case of error.
+bool SetWorkDir(const std::string &path)
+{
+ // In the ANSI version of this function, the name is limited to MAX_PATH characters.
+ // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcurrentdirectory
+ if (path.size() >= MAX_PATH)
+ return false;
+
+ return SetCurrentDirectoryA(path.c_str());
+}
+
+} // ::netcoredbg
+#endif
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file filesystem_win32.h This file contains windows-specific details to FileSystem class.
+
+#ifdef WIN32
+#pragma once
+#include <cstddef>
+#include <windows.h>
+
+namespace netcoredbg
+{
+ template <> struct FileSystemTraits<Win32PlatformTag>
+ {
+ const static size_t PathMax = MAX_PATH;
+ const static size_t NameMax = MAX_PATH - 1; // not include terminal null.
+ const static char PathSeparator = '\\';
+ const static char* PathSeparatorSymbols;
+ };
+}
+#endif
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file interop_unix.h This file contains unix-specific functions for Interop class defined in interop.h
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#include <dirent.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <set>
+
+#include "managed/interop.h"
+#include "utils/filesystem.h"
+
+namespace netcoredbg
+{
+
+// This function searches *.dll files in specified directory and adds full paths to files
+// to colon-separated list `tpaList'.
+template <>
+void InteropTraits<UnixPlatformTag>::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList)
+{
+ const char * const tpaExtensions[] = {
+ ".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+ ".dll",
+ ".ni.exe",
+ ".exe",
+ };
+
+ DIR* dir = opendir(directory.c_str());
+ if (dir == nullptr)
+ return;
+
+ std::set<std::string> addedAssemblies;
+
+ // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+ // then files with .dll extension, etc.
+ for (size_t extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
+ {
+ const char* ext = tpaExtensions[extIndex];
+ int extLength = strlen(ext);
+
+ struct dirent* entry;
+
+ // For all entries in the directory
+ while ((entry = readdir(dir)) != nullptr)
+ {
+ // We are interested in files only
+ switch (entry->d_type)
+ {
+ case DT_REG:
+ break;
+
+ // Handle symlinks and file systems that do not support d_type
+ case DT_LNK:
+ case DT_UNKNOWN:
+ {
+ std::string fullFilename;
+
+ fullFilename.append(directory);
+ fullFilename += FileSystem::PathSeparator;
+ fullFilename.append(entry->d_name);
+
+ struct stat sb;
+ if (stat(fullFilename.c_str(), &sb) == -1)
+ continue;
+
+ if (!S_ISREG(sb.st_mode))
+ continue;
+ }
+ break;
+
+ default:
+ continue;
+ }
+
+ std::string filename(entry->d_name);
+
+ // Check if the extension matches the one we are looking for
+ int extPos = filename.length() - extLength;
+ if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
+ {
+ continue;
+ }
+
+ std::string filenameWithoutExt(filename.substr(0, extPos));
+
+ // Make sure if we have an assembly with multiple extensions present,
+ // we insert only one version of it.
+ if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+ {
+ addedAssemblies.insert(filenameWithoutExt);
+
+ tpaList.append(directory);
+ tpaList += FileSystem::PathSeparator;
+ tpaList.append(filename);
+ tpaList.append(":");
+ }
+ }
+
+ // Rewind the directory stream to be able to iterate over it for the next extension
+ rewinddir(dir);
+ }
+
+ closedir(dir);
+}
+
+// This function unsets `CORECLR_ENABLE_PROFILING' environment variable.
+template <>
+void InteropTraits<UnixPlatformTag>::UnsetCoreCLREnv()
+{
+ unsetenv("CORECLR_ENABLE_PROFILING");
+}
+
+// Returns the length of a BSTR.
+template <>
+UINT InteropTraits<UnixPlatformTag>::SysStringLen(BSTR bstrString)
+{
+ if (bstrString == NULL)
+ return 0;
+ return (unsigned int)((((DWORD FAR*)bstrString)[-1]) / sizeof(OLECHAR));
+}
+
+} // ::netcoredbg
+#endif // __unix__
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file interop_win32.h This file contains windows-specific functions for Interop class defined in interop.h
+
+#ifdef WIN32
+#include <windows.h>
+#include <stddef.h>
+#include <string.h>
+#include <string>
+#include <set>
+
+#include "managed/interop.h"
+#include "utils/filesystem.h"
+#include "utils/limits.h"
+
+
+namespace netcoredbg
+{
+
+// This function searches *.dll files in specified directory and adds full paths to files
+// to semicolon-separated list `tpaList'.
+template <>
+void InteropTraits<Win32PlatformTag>::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList)
+{
+ const char * const tpaExtensions[] = {
+ "*.ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+ "*.dll",
+ "*.ni.exe",
+ "*.exe",
+ };
+
+ std::set<std::string> addedAssemblies;
+
+ // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+ // then files with .dll extension, etc.
+ for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
+ {
+ const char* ext = tpaExtensions[extIndex];
+ size_t extLength = strlen(ext);
+
+ std::string assemblyPath(directory);
+ assemblyPath += FileSystem::PathSeparator;
+ assemblyPath.append(tpaExtensions[extIndex]);
+
+ WIN32_FIND_DATAA data;
+ HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
+
+ if (findHandle != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ {
+
+ std::string filename(data.cFileName);
+ size_t extPos = filename.length() - extLength;
+ std::string filenameWithoutExt(filename.substr(0, extPos));
+
+ // Make sure if we have an assembly with multiple extensions present,
+ // we insert only one version of it.
+ if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+ {
+ addedAssemblies.insert(filenameWithoutExt);
+
+ tpaList.append(directory);
+ tpaList += FileSystem::PathSeparator;
+ tpaList.append(filename);
+ tpaList.append(";");
+ }
+ }
+ }
+ while (0 != FindNextFileA(findHandle, &data));
+
+ FindClose(findHandle);
+ }
+ }
+}
+
+// This function unsets `CORECLR_ENABLE_PROFILING' environment variable.
+template <>
+void InteropTraits<Win32PlatformTag>::UnsetCoreCLREnv()
+{
+ _putenv("CORECLR_ENABLE_PROFILING=");
+}
+
+// Returns the length of a BSTR.
+template <>
+UINT InteropTraits<Win32PlatformTag>::SysStringLen(BSTR bstrString)
+{
+ return ::SysStringLen(bstrString);
+}
+
+} // ::netcoredbg
+#endif // WIN32
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file iosystem_unix.cpp This file contains unix-specific definitions of
+/// IOSystem class members (see iosystem.h).
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#include <cstdlib>
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdexcept>
+#include <algorithm>
+
+#include "iosystem_unix.h"
+
+namespace
+{
+ // short alias for full class name
+ typedef netcoredbg::IOSystemTraits<netcoredbg::UnixPlatformTag> Class;
+
+
+ struct AsyncRead
+ {
+ int fd;
+ void* buffer;
+ size_t size;
+
+ AsyncRead(int fd, void *buf, size_t size) : fd(fd), buffer(buf), size(size) {}
+
+ Class::IOResult operator()()
+ {
+ // TODO need to optimize code to left only one syscall.
+ fd_set set;
+ FD_ZERO(&set);
+ FD_SET(fd, &set);
+ struct timeval tv = {0, 0};
+ ssize_t result = ::select(fd + 1, &set, NULL, &set, &tv);
+ if (result == 0)
+ return {Class::IOResult::Pending, 0};
+
+ if (result >= 0)
+ result = read(fd, buffer, size);
+
+ if (result < 0)
+ {
+ if (errno == EAGAIN)
+ return {Class::IOResult::Pending, 0};
+
+ // TODO make exception class
+ char msg[256];
+ snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
+ throw std::runtime_error(msg);
+ }
+
+ return {result == 0 ? Class::IOResult::Eof : Class::IOResult::Success, size_t(result)};
+ }
+
+ int poll(fd_set* read, fd_set *, fd_set* except) const
+ {
+ FD_SET(fd, read);
+ FD_SET(fd, except);
+ return fd;
+ }
+ };
+
+ struct AsyncWrite
+ {
+ int fd;
+ void const* buffer;
+ size_t size;
+
+ AsyncWrite(int fd, const void *buf, size_t size) : fd(fd), buffer(buf), size(size) {}
+
+ Class::IOResult operator()()
+ {
+ fd_set set;
+ FD_ZERO(&set);
+ FD_SET(fd, &set);
+ struct timeval tv = {0, 0};
+ ssize_t result = select(fd + 1, NULL, &set, NULL, &tv);
+ if (result == 0)
+ return {Class::IOResult::Pending, 0};
+
+ if (result >= 0)
+ result = write(fd, buffer, size);
+
+ if (result < 0)
+ {
+ if (errno == EAGAIN)
+ return {Class::IOResult::Pending, 0};
+
+ char msg[256];
+ snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
+ throw std::runtime_error(msg);
+ }
+
+ return {Class::IOResult::Success, size_t(result)};
+ }
+
+
+ int poll(fd_set *, fd_set *write, fd_set *) const
+ {
+ FD_SET(fd, write);
+ return fd;
+ }
+ };
+}
+
+
+template <typename T> Class::AsyncHandle::Traits Class::AsyncHandle::TraitsImpl<T>::traits =
+{
+ [](void *thiz)
+ -> Class::IOResult { return reinterpret_cast<T*>(thiz)->operator()(); },
+
+ [](void *thiz, fd_set* read, fd_set* write, fd_set* except)
+ -> int { return reinterpret_cast<T*>(thiz)->poll(read, write, except); },
+
+ [](void *src, void *dst)
+ -> void { *reinterpret_cast<T*>(dst) = *reinterpret_cast<T*>(src); },
+
+ [](void *thiz)
+ -> void { reinterpret_cast<T*>(thiz)->~T(); }
+};
+
+
+// Function should create unnamed pipe and return two file handles
+// (reading and writing pipe ends) or return empty file handles if pipe can't be created.
+std::pair<Class::FileHandle, Class::FileHandle> Class::unnamed_pipe()
+{
+ int fds[2];
+ if (::pipe(fds) < 0)
+ {
+ perror("pipe");
+ return {};
+ }
+
+ // TODO what to do with this?
+ signal(SIGPIPE, SIG_IGN);
+
+ return { fds[0], fds[1] };
+}
+
+
+// Function creates listening TCP socket on given port, waits, accepts single
+// connection, and return file descriptor related to the accepted connection.
+// In case of error, empty file handle will be returned.
+Class::FileHandle Class::listen_socket(unsigned port)
+{
+ assert(port > 0 && port < 65536);
+
+ int newsockfd;
+ socklen_t clilen;
+ struct sockaddr_in serv_addr, cli_addr;
+
+ int sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
+ if (sockFd < 0)
+ {
+ perror("can't create socket");
+ return {};
+ }
+
+ int enable = 1;
+ if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
+ {
+ ::close(sockFd);
+ perror("can't set socket options");
+ return {};
+ }
+ memset(&serv_addr, 0, sizeof(serv_addr));
+
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = INADDR_ANY;
+ serv_addr.sin_port = htons(port);
+
+ if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
+ {
+ ::close(sockFd);
+ perror("can't bind to specified port");
+ return {};
+ }
+
+ ::listen(sockFd, 1);
+
+#ifdef DEBUGGER_FOR_TIZEN
+ // On Tizen, launch_app won't terminate until stdin, stdout and stderr are closed.
+ // But Visual Studio initiates the connection only after launch_app termination,
+ // therefore we need to close the descriptors before the call to accept().
+ int fd_null = open("/dev/null", O_WRONLY | O_APPEND);
+ if (fd_null < 0)
+ {
+ ::close(sockFd);
+ perror("can't open /dev/null");
+ return {};
+ }
+
+ // Silently close previous stdin/stdout/stderr and reuse fds.
+ // Don't allow stdin read (EOF), but allow stdout/stderr write.
+ if (dup2(fd_null, STDIN_FILENO) == -1 ||
+ dup2(fd_null, STDOUT_FILENO) == -1 ||
+ dup2(fd_null, STDERR_FILENO) == -1)
+ {
+ ::close(sockFd);
+ perror("can't dup2");
+ return {};
+ }
+
+ close(fd_null);
+
+ //TODO on Tizen redirect stderr/stdout output into dlog
+#endif
+
+ clilen = sizeof(cli_addr);
+ newsockfd = ::accept(sockFd, (struct sockaddr *) &cli_addr, &clilen);
+ ::close(sockFd);
+ if (newsockfd < 0)
+ {
+ perror("accept");
+ return {};
+ }
+
+ return newsockfd;
+}
+
+// Enable/disable handle inheritance for child processes.
+Class::IOResult Class::set_inherit(const FileHandle &fh, bool inherit)
+{
+ int flags = fcntl(fh.fd, F_GETFD);
+ if (flags < 0)
+ return {IOResult::Error, 0};
+
+ if (inherit)
+ flags &= ~FD_CLOEXEC;
+ else
+ flags |= FD_CLOEXEC;
+
+ if (fcntl(fh.fd, F_SETFD, flags) < 0)
+ return {IOResult::Error, 0};
+
+ return {IOResult::Success, 0};
+}
+
+// Function perform reading from the file: it may read up to `count' bytes to `buf'.
+Class::IOResult Class::read(const FileHandle &fh, void *buf, size_t count)
+{
+ ssize_t rsize = ::read(fh.fd, buf, count);
+ if (rsize < 0)
+ return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 };
+ else
+ return { (rsize == 0 ? IOResult::Eof : IOResult::Success), size_t(rsize) };
+}
+
+
+// Function perform writing to the file: it may write up to `count' byte from `buf'.
+Class::IOResult Class::write(const FileHandle &fh, const void *buf, size_t count)
+{
+ ssize_t wsize = ::write(fh.fd, buf, count);
+ if (wsize < 0)
+ return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 };
+ else
+ return { IOResult::Success, size_t(wsize) };
+}
+
+
+Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count)
+{
+ return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create<AsyncRead>(fh.fd, buf, count);
+}
+
+Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count)
+{
+ return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create<AsyncWrite>(fh.fd, buf, count);
+}
+
+
+bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout)
+{
+ fd_set read_set, write_set, except_set;
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ FD_ZERO(&except_set);
+
+ int maxfd = -1;
+ for (IOSystem::AsyncHandleIterator it = begin; it != end; ++it)
+ {
+ if (*it)
+ maxfd = std::max(it->handle.poll(&read_set, &write_set, &except_set), maxfd);
+ }
+
+ struct timeval tv;
+ std::chrono::microseconds us = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
+ tv.tv_sec = us.count() / 1000000, tv.tv_usec = us.count() % 1000000;
+
+ int result;
+ do result = ::select(maxfd + 1, &read_set, &write_set, &except_set, &tv);
+ while (result < 0 && errno == EINTR);
+
+ if (result < 0)
+ {
+ char msg[256];
+ snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
+ throw std::runtime_error(msg);
+ }
+
+ return result > 0;
+}
+
+Class::IOResult Class::async_cancel(Class::AsyncHandle& handle)
+{
+ if (!handle)
+ return {Class::IOResult::Error, 0};
+
+ handle = {};
+ return {Class::IOResult::Success, 0};
+}
+
+Class::IOResult Class::async_result(Class::AsyncHandle& handle)
+{
+ if (!handle)
+ return {Class::IOResult::Error, 0};
+
+ auto result = handle();
+ if (result.status != Class::IOResult::Pending)
+ handle = {};
+
+ return result;
+}
+
+
+// Function closes the file represented by file handle.
+Class::IOResult Class::close(const FileHandle &fh)
+{
+ return { (::close(fh.fd) == 0 ? IOResult::Success : IOResult::Error), 0 };
+}
+
+
+// This function returns triplet of currently selected standard files.
+netcoredbg::IOSystem::StdFiles Class::get_std_files()
+{
+ static const IOSystem::FileHandle handles[std::tuple_size<IOSystem::StdFiles>::value] = {
+ FileHandle(STDIN_FILENO), FileHandle(STDOUT_FILENO), FileHandle(STDERR_FILENO)
+ };
+ return {handles[0], handles[1], handles[2]};
+}
+
+
+// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor.
+// Substitution exists only during life time of StsIOSwap instance.
+Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true)
+{
+ const static unsigned NFD = std::tuple_size<StdFiles>::value;
+ static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr };
+
+ const int newfd[NFD] = {
+ std::get<StdFileType::Stdin>(files).handle.fd,
+ std::get<StdFileType::Stdout>(files).handle.fd,
+ std::get<StdFileType::Stderr>(files).handle.fd };
+
+ for (unsigned n = 0; n < NFD; n++)
+ {
+ m_orig_fd[n] = ::dup(oldfd[n]);
+ if (m_orig_fd[n] == -1)
+ {
+ char msg[256];
+ snprintf(msg, sizeof(msg), "dup(%d): %s", oldfd[n], strerror(errno));
+ throw std::runtime_error(msg);
+ }
+
+ if (::dup2(newfd[n], oldfd[n]) == -1)
+ {
+ char msg[256];
+ snprintf(msg, sizeof(msg), "dup2(%d, %d): %s", newfd[n], oldfd[n], strerror(errno));
+ throw std::runtime_error(msg);
+ }
+ }
+}
+
+
+Class::StdIOSwap::~StdIOSwap()
+{
+ if (!m_valid)
+ return;
+
+ const static unsigned NFD = std::tuple_size<StdFiles>::value;
+ static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr };
+ for (unsigned n = 0; n < NFD; n++)
+ {
+ if (::dup2(m_orig_fd[n], oldfd[n]) == -1)
+ {
+ abort();
+ }
+
+ ::close(m_orig_fd[n]);
+ }
+}
+
+#endif // __unix__
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file iosystem_unix.h This file contains unix-specific declaration of IOSystem class (see iosystem.h).
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#pragma once
+#include <cstdlib>
+#include <cassert>
+#include <sys/select.h>
+#include <tuple>
+#include <new>
+
+#include "utils/platform.h"
+#include "utils/iosystem.h"
+
+template <> struct netcoredbg::IOSystemTraits<netcoredbg::UnixPlatformTag>
+{
+ using IOSystem = typename netcoredbg::IOSystemImpl<IOSystemTraits<UnixPlatformTag> >;
+ using IOResult = IOSystem::IOResult;
+
+ struct FileHandle
+ {
+ FileHandle() : fd(-1) {}
+ FileHandle(int n) : fd(n) {}
+ explicit operator bool() const { return fd != -1; }
+
+ int fd;
+ };
+
+ struct AsyncHandle
+ {
+ struct Traits
+ {
+ IOResult (*oper)(void *thiz);
+ int (*poll)(void *thiz, fd_set *, fd_set *, fd_set *);
+ void (*move)(void* src, void *dst);
+ void (*destr)(void *thiz);
+ };
+
+ template <typename T> struct TraitsImpl
+ {
+ static struct Traits traits;
+ };
+
+ const Traits *traits;
+ mutable char data alignas(__BIGGEST_ALIGNMENT__) [sizeof(void*) * 4];
+
+ explicit operator bool() const { return !!traits; }
+
+ IOResult operator()() { assert(*this); return traits->oper(data); }
+
+ int poll(fd_set* read, fd_set* write, fd_set* except)
+ {
+ assert(*this);
+ return traits->poll(data, read, write, except);
+ }
+
+ AsyncHandle() : traits(nullptr) {}
+
+ template <typename InstanceType, typename... Args>
+ static AsyncHandle create(Args&&... args)
+ {
+ static_assert(sizeof(InstanceType) <= sizeof(data), "insufficiend data size");
+ AsyncHandle result;
+ result.traits = &TraitsImpl<InstanceType>::traits;
+ new (result.data) InstanceType(std::forward<Args>(args)...);
+ return result;
+ }
+
+ AsyncHandle(AsyncHandle&& other) : traits(other.traits)
+ {
+ if (other) traits->move(other.data, data);
+ other.traits = nullptr;
+ }
+
+ AsyncHandle& operator=(AsyncHandle&& other)
+ {
+ this->~AsyncHandle();
+ return *new (this) AsyncHandle(std::move(other));
+ }
+
+ ~AsyncHandle() { if (*this) traits->destr(data); }
+ };
+
+ static std::pair<FileHandle, FileHandle> unnamed_pipe();
+ static FileHandle listen_socket(unsigned tcp_port);
+ static IOResult set_inherit(const FileHandle&, bool);
+ static IOResult read(const FileHandle&, void *buf, size_t count);
+ static IOResult write(const FileHandle&, const void *buf, size_t count);
+ static AsyncHandle async_read(const FileHandle&, void *buf, size_t count);
+ static AsyncHandle async_write(const FileHandle&, const void *buf, size_t count);
+ static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds);
+ static IOResult async_cancel(AsyncHandle&);
+ static IOResult async_result(AsyncHandle&);
+ static IOResult close(const FileHandle&);
+
+ struct StdIOSwap
+ {
+ using StdFiles = IOSystem::StdFiles;
+ using StdFileType = IOSystem::StdFileType;
+ StdIOSwap(const StdFiles &);
+ ~StdIOSwap();
+
+ StdIOSwap(StdIOSwap&& other)
+ {
+ m_valid = other.m_valid;
+ if (!m_valid)
+ return;
+
+ other.m_valid = false;
+ for (unsigned n = 0; n < std::tuple_size<StdFiles>::value; n++)
+ m_orig_fd[n] = other.m_orig_fd[n];
+ }
+
+ bool m_valid;
+ int m_orig_fd[std::tuple_size<StdFiles>::value];
+ };
+
+ static IOSystem::StdFiles get_std_files();
+};
+
+#endif // __unix__
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file iosystem_win32.cpp This file contains windows-specific definitions of
+/// IOSystem class members (see iosystem.h).
+
+#ifdef WIN32
+#include <io.h>
+#include <fcntl.h>
+#include <ws2tcpip.h>
+#include <afunix.h>
+#include <stdexcept>
+#include <new>
+#include <memory>
+#include <atomic>
+#include <string.h>
+#include <assert.h>
+#include "utils/iosystem.h"
+#include "utils/limits.h"
+
+// short alias for full class name
+namespace { typedef netcoredbg::IOSystemTraits<netcoredbg::Win32PlatformTag> Class; }
+
+namespace
+{
+ class Win32Exception : public std::runtime_error
+ {
+ struct Msg
+ {
+ mutable char buf[2 * LINE_MAX];
+ };
+
+ static const char* getmsg(const char *prefix, DWORD error, const Msg& msg = Msg())
+ {
+ int len = prefix ? snprintf(msg.buf, sizeof(msg.buf), "%s: ", prefix) : 0;
+
+ if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ msg.buf + len, sizeof(msg.buf) - len, NULL))
+ {
+ return msg.buf;
+ }
+
+ snprintf(msg.buf + len, sizeof(msg.buf) - len, "error %#x", error);
+ return msg.buf;
+ }
+
+ public:
+ /// Specify Win32 error code and, optionally, error message prefix.
+ Win32Exception(DWORD error, const char* prefix = nullptr) : std::runtime_error(getmsg(prefix, error)) {}
+
+ /// Specify error message prefix (optionally). Win32 error code will be obtained via call to GetLastError().
+ Win32Exception(const char *prefix = nullptr) : Win32Exception(prefix, GetLastError()) {}
+
+ /// Specify explicitly error message prefix and error code.
+ Win32Exception(const char *prefix, DWORD error) : Win32Exception(error, prefix) {}
+ };
+
+ struct Initializer
+ {
+ Initializer()
+ {
+ WSADATA wsa;
+ int wsa_error = WSAStartup(MAKEWORD(2, 2), &wsa);
+ if (wsa_error != 0)
+ throw Win32Exception("WSAStartup failed", wsa_error);
+ }
+
+ ~Initializer()
+ {
+ WSACleanup();
+ }
+ };
+
+ static Initializer initializer;
+
+#if 0
+ // assuming domain=AF_UNIX, type=SOCK_STREAM, protocol=0
+ int wsa_socketpair(int domain, int type, int protocol, SOCKET sv[2])
+ {
+
+ SOCKET serv = ::socket(domain, type, protocol);
+ if (serv == INVALID_SOCKET)
+ throw Win32Exception("can't create socket", WSAGetLastError());
+
+ // TODO
+ char name[] = "netcoredbg";
+ size_t namelen = sizeof(name)-1;
+
+ SOCKADDR_UN sa;
+ sa.sun_family = domain;
+ assert(namelen <= sizeof(sa.sun_path));
+ memcpy(sa.sun_path, name, namelen);
+ if (::bind(serv, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
+ {
+ auto err = WSAGetLastError();
+ ::closesocket(serv);
+ throw Win32Exception("can't bind socket", err);
+ }
+
+ u_long mode = 1;
+ if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR)
+ {
+ auto err = WSAGetLastError();
+ ::closesocket(serv);
+ throw Win32Exception("ioctlsocket(FIONBIO)", err);
+ }
+
+ if (::listen(serv, 1) == SOCKET_ERROR && WSAGetLastError() != WSAEINPROGRESS)
+ {
+ auto err = WSAGetLastError();
+ ::closesocket(serv);
+ throw Win32Exception("ioctlsocket(FIONBIO)", err);
+ }
+
+ SOCKET conn = ::socket(domain, type, protocol);
+ if (conn == INVALID_SOCKET)
+ {
+ auto err = WSAGetLastError();
+ ::closesocket(serv);
+ throw Win32Exception("can't create socket", err);
+ }
+
+ sa.sun_family = domain;
+ memcpy(sa.sun_path, name, namelen);
+ if (::connect(conn, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
+ {
+ auto err = WSAGetLastError();
+ ::closesocket(serv);
+ ::closesocket(conn);
+ throw Win32Exception("can't bind socket", err);
+ }
+
+ mode = 0;
+ if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR)
+ {
+ auto err = WSAGetLastError();
+ ::closesocket(serv);
+ ::closesocket(conn);
+ throw Win32Exception("ioctlsocket(FIONBIO)", err);
+ }
+
+ SOCKET newsock = ::accept(serv, NULL, NULL);
+ if (newsock == INVALID_SOCKET)
+ {
+ auto err = WSAGetLastError();
+ ::closesocket(serv);
+ ::closesocket(conn);
+ throw Win32Exception("accept on socket", err);
+ }
+
+ ::closesocket(serv);
+
+ sv[0] = newsock, sv[1] = conn;
+ return 0;
+ }
+#endif
+}
+
+
+// Function should create unnamed pipe and return two file handles
+// (reading and writing pipe ends) or return empty file handles if pipe can't be created.
+std::pair<Class::FileHandle, Class::FileHandle> Class::unnamed_pipe()
+{
+#if 0
+ SOCKET sv[2];
+ if (wsa_socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0)
+ return {FileHandle(), FileHandle()};
+#endif
+
+ static const size_t PipeSize = 32 * LINE_MAX;
+
+ SECURITY_ATTRIBUTES saAttr;
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ HANDLE reading_fd, writing_fd;
+
+ static std::atomic<long> pipe_num;
+ char pipe_name[MAX_PATH + 1];
+ snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\Pipe\\Win32Pipes.%08x.%08x",
+ GetCurrentProcessId(), pipe_num++);
+
+ reading_fd = CreateNamedPipeA(pipe_name,
+ PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_WAIT,
+ 1, // number of pipes
+ PipeSize, PipeSize,
+ 0, // 50ms default timeout
+ &saAttr);
+
+ if (reading_fd == INVALID_HANDLE_VALUE)
+ {
+ perror("CreateNamedPipeA");
+ return { FileHandle(), FileHandle() };
+ }
+
+ writing_fd = CreateFileA(pipe_name,
+ GENERIC_WRITE,
+ 0, // no sharing
+ &saAttr,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ NULL);
+
+ if (writing_fd == INVALID_HANDLE_VALUE)
+ {
+ auto err = GetLastError();
+ ::CloseHandle(writing_fd);
+ fprintf(stderr, "CreateFile pipe error: %#x\n", err);
+ return { FileHandle(), FileHandle() };
+ }
+
+ if (!SetHandleInformation(writing_fd, HANDLE_FLAG_INHERIT, 0))
+ {
+ fprintf(stderr, "SetHandleInformation failed!\n");
+ return { FileHandle(), FileHandle() };
+ }
+
+ if (!SetHandleInformation(reading_fd, HANDLE_FLAG_INHERIT, 0))
+ {
+ fprintf(stderr, "SetHandleInformation failed!\n");
+ return { FileHandle(), FileHandle() };
+ }
+
+ return { FileHandle(reading_fd), FileHandle(writing_fd) };
+}
+
+
+// Function creates listening TCP socket on given port, waits, accepts single
+// connection, and return file descriptor related to the accepted connection.
+// In case of error, empty file handle will be returned.
+Class::FileHandle Class::listen_socket(unsigned port)
+{
+ assert(port > 0 && port < 65536);
+
+ SOCKET newsockfd;
+ int clilen;
+ struct sockaddr_in serv_addr, cli_addr;
+
+ SOCKET sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
+ if (sockFd == INVALID_SOCKET)
+ {
+ fprintf(stderr, "can't create socket: %#x\n", WSAGetLastError());
+ return {};
+ }
+
+ BOOL enable = 1;
+ if (::setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, (const char *)&enable, sizeof(BOOL)) == SOCKET_ERROR)
+ {
+ ::closesocket(sockFd);
+ fprintf(stderr, "setsockopt failed\n");
+ return {};
+ }
+ memset(&serv_addr, 0, sizeof(serv_addr));
+
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = INADDR_ANY;
+ serv_addr.sin_port = htons(port);
+
+ if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
+ {
+ ::closesocket(sockFd);
+ fprintf(stderr, "can't bind to specified port!\n");
+ return {};
+ }
+
+ ::listen(sockFd, 1);
+
+ clilen = sizeof(cli_addr);
+ newsockfd = ::accept(sockFd, (struct sockaddr*)&cli_addr, &clilen);
+ ::closesocket(sockFd);
+ if (newsockfd == INVALID_SOCKET)
+ {
+ fprintf(stderr, "can't accept connection\n");
+ return {};
+ }
+
+ return FileHandle(newsockfd);
+}
+
+// Function enables or disables inheritance of file handle for child processes.
+Class::IOResult Class::set_inherit(const FileHandle& fh, bool inherit)
+{
+ DWORD flags;
+ if (!GetHandleInformation(fh.handle, &flags))
+ return {IOResult::Error};
+
+ if (inherit)
+ flags |= HANDLE_FLAG_INHERIT;
+ else
+ flags &= ~HANDLE_FLAG_INHERIT;
+
+ if (!SetHandleInformation(fh.handle, HANDLE_FLAG_INHERIT, flags))
+ return {IOResult::Error};
+
+ return {IOResult::Success};
+}
+
+// Function perform reading from the file: it may read up to `count' bytes to `buf'.
+Class::IOResult Class::read(const FileHandle& fh, void *buf, size_t count)
+{
+ DWORD dwRead = 0;
+ OVERLAPPED ov = {};
+ if (! ReadFile(fh.handle, buf, (DWORD)count, &dwRead, &ov))
+ return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwRead };
+ else
+ return { (dwRead == 0 ? IOResult::Eof : IOResult::Success), dwRead };
+}
+
+
+// Function perform writing to the file: it may write up to `count' byte from `buf'.
+Class::IOResult Class::write(const FileHandle& fh, const void *buf, size_t count)
+{
+ // see https://stackoverflow.com/questions/43939424/writefile-with-windows-sockets-returns-invalid-parameter-error
+ DWORD dwWritten = 0;
+ OVERLAPPED ov = {};
+ if (! WriteFile(fh.handle, buf, (DWORD)count, &dwWritten, &ov))
+ return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwWritten };
+ else
+ return { IOResult::Success, dwWritten };
+}
+
+
+Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count)
+{
+ if (fh.handle == INVALID_HANDLE_VALUE)
+ return {};
+
+ AsyncHandle result;
+ result.check_eof = true;
+ result.handle = fh.handle;
+ result.overlapped.reset(new OVERLAPPED);
+ memset(result.overlapped.get(), 0, sizeof(OVERLAPPED));
+ result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (result.overlapped->hEvent == INVALID_HANDLE_VALUE)
+ return {};
+
+ DWORD val;
+ DWORD bytesRead;
+
+ if (GetConsoleMode(fh.handle, &val))
+ { // file handle is the console
+ // first, remove all events before the first key event, if exists
+ while (GetNumberOfConsoleInputEvents(fh.handle, &val) && val)
+ {
+ INPUT_RECORD event;
+ if (!PeekConsoleInput(fh.handle, &event, 1, &bytesRead))
+ return {};
+ if (event.EventType != KEY_EVENT || (event.EventType == KEY_EVENT && !event.Event.KeyEvent.bKeyDown))
+ {
+ if (!ReadConsoleInput(fh.handle, &event, 1, &bytesRead))
+ return {};
+ }
+ else
+ break;
+ }
+ if (!val)
+ {
+ // nothing to read from the console -- defer call to ReadFile
+ result.buf = buf, result.count = count;
+ return result;
+ }
+ }
+
+ if (! ReadFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get()))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ return {};
+ }
+
+ return result;
+}
+
+Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count)
+{
+ if (fh.handle == INVALID_HANDLE_VALUE)
+ return {};
+
+ AsyncHandle result;
+ result.handle = fh.handle;
+ result.overlapped.reset(new OVERLAPPED);
+ memset(result.overlapped.get(), 0, sizeof(OVERLAPPED));
+ result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (result.overlapped->hEvent == INVALID_HANDLE_VALUE)
+ return {};
+
+ if (! WriteFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get()))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ return {};
+ }
+
+ return result;
+}
+
+bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout)
+{
+ // console workaround
+ for (auto it = begin; it != end; ++it)
+ {
+ if (it->handle.buf)
+ {
+ DWORD val;
+ if (GetNumberOfConsoleInputEvents(it->handle.handle, &val) && val)
+ SetEvent(it->handle.overlapped->hEvent);
+ }
+ }
+
+ // count number of active handles
+ unsigned count = 0;
+ for (auto it = begin; it != end; ++it)
+ if (*it) ++count;
+
+ // allocate memory for events array
+ HANDLE *events = static_cast<HANDLE*>(alloca(count * sizeof(HANDLE)));
+ unsigned n = 0;
+ for (auto it = begin; it != end; ++it)
+ {
+ if (*it)
+ events[n++] = it->handle.overlapped->hEvent;
+ }
+
+ assert(n == count);
+ DWORD result = WaitForMultipleObjects(count, events, FALSE, DWORD(timeout.count()));
+ return result != WAIT_FAILED && result != WAIT_TIMEOUT;
+}
+
+Class::IOResult Class::async_cancel(AsyncHandle& h)
+{
+ if (!h)
+ return {IOResult::Error};
+
+ if (!CloseHandle(h.overlapped->hEvent))
+ perror("CloseHandle(event) error");
+
+ // console workaround -- canceling deffered operation
+ if (h.buf)
+ {
+ h = AsyncHandle();
+ return {IOResult::Success};
+ }
+
+ IOResult result;
+ if (!CancelIoEx(h.handle, h.overlapped.get()))
+ result = {IOResult::Error};
+ else
+ result = {IOResult::Success};
+
+ h = AsyncHandle();
+ return result;
+}
+
+Class::IOResult Class::async_result(AsyncHandle& h)
+{
+ if (!h)
+ return {IOResult::Error};
+
+ DWORD bytes = 1;
+ bool finished;
+
+ if (h.buf)
+ {
+ // workaround for the console
+ finished = true;
+ }
+ else
+ {
+ // pipes, normal files, etc...
+ finished = GetOverlappedResult(h.handle, h.overlapped.get(), &bytes, FALSE);
+ if (!finished)
+ {
+ DWORD error = GetLastError();
+ if (error == ERROR_IO_INCOMPLETE)
+ return {IOResult::Pending};
+ }
+ }
+
+ if (!CloseHandle(h.overlapped->hEvent))
+ perror("CloseHandle(event) error");
+
+ bool check_eof = h.check_eof;
+
+ h = AsyncHandle();
+
+ if (!finished)
+ return {IOResult::Error};
+
+ if (check_eof && bytes == 0)
+ return {IOResult::Eof, bytes};
+
+ return {IOResult::Success, bytes};
+}
+
+// Function closes the file represented by file handle.
+Class::IOResult Class::close(const FileHandle& fh)
+{
+ assert(fh);
+ if (fh.type == FileHandle::Socket)
+ return { ::closesocket((SOCKET)fh.handle) == 0 ? IOResult::Success : IOResult::Error };
+ else
+ return { ::CloseHandle(fh.handle) ? IOResult::Success : IOResult::Error };
+}
+
+
+// Function allows non-blocking IO on files, it is similar with select(2) system call on Unix.
+// Arguments includes: pointers to three sets of file handles (for reading, for writing, and for
+// exceptions), and timeout value, in milliseconds. Any pointer might have NULL value if some set
+// isn't specified.
+// Function returns -1 on error, 0 on timeout or number of ready to read/write file handles.
+// If function returns value greater than zero, at least one of the sets, passed in arguments,
+// is not empty and contains file handles ready to read/write/etc...
+
+
+// This function returns triplet of currently selected standard files.
+Class::IOSystem::StdFiles Class::get_std_files()
+{
+ using Handles = std::tuple<IOSystem::FileHandle, IOSystem::FileHandle, IOSystem::FileHandle>;
+ /*thread_local*/ static alignas(alignof(Handles)) char mem[sizeof(Handles)]; // TODO
+ Handles& handles = *new (mem) Handles {
+ FileHandle(GetStdHandle(STD_INPUT_HANDLE)),
+ FileHandle(GetStdHandle(STD_OUTPUT_HANDLE)),
+ FileHandle(GetStdHandle(STD_ERROR_HANDLE))
+ };
+ return { std::get<IOSystem::Stdin>(handles),
+ std::get<IOSystem::Stdout>(handles),
+ std::get<IOSystem::Stderr>(handles) };
+}
+
+
+// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor.
+// Substitution exists only during life time of StsIOSwap instance.
+Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true)
+{
+ const static unsigned NFD = std::tuple_size<StdFiles>::value;
+ static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
+ static const int open_flags[NFD] = {_O_RDONLY | _O_BINARY, _O_BINARY, _O_BINARY};
+ const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)};
+
+ const FileHandle new_handles[NFD] = {
+ std::get<IOSystem::Stdin>(files).handle,
+ std::get<IOSystem::Stdout>(files).handle,
+ std::get<IOSystem::Stderr>(files).handle };
+
+ fflush(stdout);
+ fflush(stderr);
+
+ for (unsigned n = 0; n < NFD; n++)
+ {
+ if (new_handles[n].type != FileHandle::FileOrPipe)
+ throw std::runtime_error("can't use socket handle for stdin/stdout/stderr");
+ }
+
+ for (unsigned n = 0; n < NFD; n++)
+ {
+ m_orig_handle[n] = GetStdHandle(std_handles[n]);
+ if (m_orig_handle[n] == INVALID_HANDLE_VALUE)
+ {
+ char msg[256];
+ snprintf(msg, sizeof(msg), "GetStdHandle(%#x): error", std_handles[n]);
+ throw std::runtime_error(msg);
+ }
+
+ if (!SetHandleInformation(new_handles[n].handle, HANDLE_FLAG_INHERIT, 1))
+ fprintf(stderr, "SetHandleInformation failed!\n");
+
+ if (!SetStdHandle(std_handles[n], new_handles[n].handle))
+ {
+ char msg[256];
+ snprintf(msg, sizeof(msg), "SetStdHandle(%#x, %p): error", std_handles[n], new_handles[n].handle);
+ throw std::runtime_error(msg);
+ }
+
+
+ int fd = _open_osfhandle(reinterpret_cast<intptr_t>(new_handles[n].handle), open_flags[n]);
+ if (fd == -1)
+ throw Win32Exception("_open_osfhandle");
+
+ m_orig_fd[n] = _dup(open_fds[n]);
+ if (m_orig_fd[n] == -1)
+ throw Win32Exception("_dup");
+
+ if (_dup2(fd, open_fds[n]) == -1)
+ throw Win32Exception("_dup2");
+
+ close(fd);
+ }
+}
+
+Class::StdIOSwap::~StdIOSwap()
+{
+ if (!m_valid)
+ return;
+
+ const static unsigned NFD = std::tuple_size<StdFiles>::value;
+ static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
+ const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)};
+
+ fflush(stdout);
+ fflush(stderr);
+
+ for (unsigned n = 0; n < NFD; n++)
+ {
+ if (!SetStdHandle(std_handles[n], m_orig_handle[n]))
+ {
+ abort();
+ }
+
+ _dup2(m_orig_fd[n], open_fds[n]);
+ close(m_orig_fd[n]);
+ }
+}
+
+#endif // WIN32
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file iosystem_win32.h This file contains windows-specific declaration of IOSystem class (see iosystem.h).
+//
+#ifdef _WIN32
+#pragma once
+#include <winsock2.h>
+#include <windows.h> // TODO
+#include <assert.h>
+#include <tuple>
+
+namespace netcoredbg
+{
+
+template <> struct IOSystemTraits<Win32PlatformTag>
+{
+ struct FileHandle
+ {
+ FileHandle() : handle(INVALID_HANDLE_VALUE), type(FileOrPipe) {}
+
+ explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; }
+
+ enum FileType
+ {
+ FileOrPipe,
+ Socket
+ };
+
+ FileHandle(HANDLE filefd) : handle(filefd), type(FileOrPipe) {}
+ FileHandle(SOCKET sockfd) : handle((HANDLE)sockfd), type(Socket) {}
+
+ HANDLE handle;
+ enum FileType type;
+ };
+
+ struct AsyncHandle
+ {
+ HANDLE handle;
+ std::unique_ptr<OVERLAPPED> overlapped;
+ bool check_eof;
+
+ // workaround: non-blocking reading from console
+ void *buf;
+ size_t count;
+
+ AsyncHandle()
+ : handle(INVALID_HANDLE_VALUE), overlapped(), check_eof(false), buf(nullptr), count(0)
+ {}
+
+ AsyncHandle(AsyncHandle&& other) noexcept
+ : handle(other.handle), overlapped(std::move(other.overlapped)), check_eof(other.check_eof),
+ buf(other.buf), count(other.count)
+ {
+ other.handle = INVALID_HANDLE_VALUE;
+ }
+
+ AsyncHandle& operator=(AsyncHandle&& other) noexcept
+ {
+ return this->~AsyncHandle(), *new (this) AsyncHandle(std::move(other));
+ }
+
+ explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; }
+ };
+
+ using IOSystem = typename IOSystemImpl<IOSystemTraits<Win32PlatformTag> >;
+ using IOResult = IOSystem::IOResult;
+
+ static std::pair<FileHandle, FileHandle> unnamed_pipe();
+ static FileHandle listen_socket(unsigned tcp_port);
+ static IOResult set_inherit(const FileHandle &, bool);
+ static IOResult read(const FileHandle &, void *buf, size_t count);
+ static IOResult write(const FileHandle &, const void *buf, size_t count);
+ static AsyncHandle async_read(const FileHandle &, void *buf, size_t count);
+ static AsyncHandle async_write(const FileHandle &, const void *buf, size_t count);
+ static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds);
+ static IOResult async_cancel(AsyncHandle &);
+ static IOResult async_result(AsyncHandle &);
+ static IOResult close(const FileHandle &);
+
+ static IOSystem::StdFiles get_std_files();
+
+ struct StdIOSwap
+ {
+ using StdFiles = IOSystem::StdFiles;
+ using StdFileType = IOSystem::StdFileType;
+ StdIOSwap(const StdFiles &);
+ ~StdIOSwap();
+
+ StdIOSwap(StdIOSwap&& other)
+ {
+ m_valid = other.m_valid;
+ if (!m_valid)
+ return;
+
+ other.m_valid = false;
+ for (unsigned n = 0; n < std::tuple_size<StdFiles>::value; n++)
+ {
+ m_orig_handle[n] = other.m_orig_handle[n];
+ m_orig_fd[n] = other.m_orig_fd[n];
+ }
+ }
+
+ bool m_valid;
+ HANDLE m_orig_handle[std::tuple_size<StdFiles>::value];
+ int m_orig_fd[std::tuple_size<StdFiles>::value];
+ };
+};
+
+} // ::netcoredbg
+#endif // WIN32
// See the LICENSE file in the project root for more information.
#pragma once
-#include "utility.h"
+#include "utils/utility.h"
#include "utils/string_view.h"
namespace netcoredbg
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file platform_unix.cpp This file contains unix-specific function definitions,
+/// for functions defined in platform.h
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#ifdef __APPLE__
+#include <crt_externs.h>
+#endif
+#include <unistd.h>
+#include "utils/platform.h"
+
+extern char** environ;
+
+namespace netcoredbg
+{
+
+// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix).
+unsigned long OSPageSize()
+{
+ static unsigned long pageSize = sysconf(_SC_PAGESIZE);
+ return pageSize;
+}
+
+
+// Function suspends process execution for specified amount of time (in microseconds)
+void USleep(unsigned long usec)
+{
+ usleep(usec);
+}
+
+
+// Function returns list of environment variables (like char **environ).
+char** GetSystemEnvironment()
+{
+#if __APPLE__
+ return *(_NSGetEnviron());
+#else // __APPLE__
+ return environ;
+#endif // __APPLE__
+}
+
+} // ::netcoredbg
+#endif // __unix__
--- /dev/null
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file platform_win32.cpp This file contains windows-specific function definitions,
+/// for functions defined in platform.h
+
+#ifdef WIN32
+#include <windows.h>
+#include <stdlib.h> // char **environ
+#include "utils/platform.h"
+#include "utils/limits.h"
+
+namespace netcoredbg
+{
+
+// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix).
+unsigned long OSPageSize()
+{
+ static unsigned long pageSize = []{
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwPageSize;
+ }();
+
+ return pageSize;
+}
+
+
+// Function suspends process execution for specified amount of time (in microseconds)
+void USleep(unsigned long usec)
+{
+ HANDLE timer;
+ LARGE_INTEGER ft;
+
+ ft.QuadPart = -(10*(long)usec); // Convert to 100 nanosecond interval, negative value indicates relative time
+
+ timer = CreateWaitableTimer(NULL, TRUE, NULL);
+ SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
+ WaitForSingleObject(timer, INFINITE);
+ CloseHandle(timer);
+}
+
+
+// Function returns list of environment variables (like char **environ).
+char** GetSystemEnvironment()
+{
+ return environ;
+}
+
+} // ::netcoredbg
+#endif
// See the LICENSE file in the project root for more information.
#pragma once
-#include "utility.h"
+#include "utils/utility.h"
#include <mutex>
#include <condition_variable>
--- /dev/null
+// Copyright (c) 2020 Samsung Electronics Co., LTD
+// Distributed under the MIT License.
+// See the LICENSE file in the project root for more information.
+
+// \file utility.h This file contains few supplimentar classes and functions residing in Utility namespace.
+
+#pragma once
+#include <stddef.h>
+
+namespace netcoredbg
+{
+
+namespace Utility
+{
+
+/// @{ This function is similar to `std::size()` from C++17 and allows to determine
+/// the object size as number of elements, which might be stored within object
+/// (opposed to sizeof(), which returns object sizes in bytes). Typically these
+/// functions applicable to arrays and to classes like std::array.
+template <typename T> constexpr auto Size(const T& v) -> decltype(v.size()) { return v.size(); }
+template <class T, size_t N> constexpr size_t Size(const T (&)[N]) noexcept { return N; }
+/// @}
+
+
+/// This type is similar to `std::index_sequence` from C++14 and typically
+/// should be used in pair with `MakeSequence` type to create sequence of
+/// indices usable in template metaprogramming techniques.
+template <size_t... Index> struct Sequence {};
+
+// Actual implementation of MakeSequence type.
+namespace Internals
+{
+ template <size_t Size, size_t... Index> struct MakeSequence : MakeSequence<Size-1, Size-1, Index...> {};
+ template <size_t... Index> struct MakeSequence<0, Index...> { typedef Sequence<Index...> type; };
+}
+
+/// MakeSequence type is similar to `std::make_index_sequence<N>` from C++14.
+/// Instantination of this type exapands to `Sequence<size_t...N>` type, where
+/// `N` have values from 0 to `Size-1`.
+template <size_t Size> using MakeSequence = typename Internals::MakeSequence<Size>::type;
+
+
+/// @{ This is similar to std:void_t which is defined in c++17.
+template <typename... Args> struct MakeVoid { typedef void type; };
+template <typename... Args> using Void = typename MakeVoid<Args...>::type;
+/// @}
+
+
+/// This template is similar to `offsetof` macros in plain C. It allows
+/// to get offset of specified member in some particular class.
+template <typename Owner, typename Member>
+static inline constexpr size_t offset_of(const Member Owner::*mem)
+{
+ return reinterpret_cast<size_t>(&(reinterpret_cast<Owner*>(0)->*mem));
+}
+
+/// This template is similar to well known `container_of` macros. It allows
+/// to get pointer to owner class from pointer to member.
+template <typename Owner, typename Member>
+static inline constexpr Owner *container_of(Member *ptr, const Member Owner::*mem)
+{
+ return reinterpret_cast<Owner*>(reinterpret_cast<size_t>(ptr) - offset_of(mem));
+}
+
+
+// This is helper class which simplifies implementation of singleton classes.
+//
+// Usage example:
+// 1) define dictinct type of singleton: typedef Singleton<YourType> YourSingleton;
+// 2) to access your singleton use expression: YourSingleton::instance().operations...
+//
+template <typename T> struct Singleton
+{
+ static T& instance()
+ {
+ static T val;
+ return val;
+ }
+};
+
+} // Utility namespace
+} // namespace netcoredbg
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file dynlibsi_win32.h This file contains windows-specific function definitions
-/// required to work with dynamically loading libraries.
-
-#ifdef WIN32
-#include <windows.h>
-#include "utils/dynlibs.h"
-#include "utils/limits.h"
-
-namespace netcoredbg
-{
-
-// This functon load specified library and returns handle (which then
-// can be passed to DLSym and DLCLose functions).
-// In case of error function returns NULL.
-DLHandle DLOpen(const std::string &path)
-{
- return reinterpret_cast<DLHandle>(::LoadLibraryExA(path.c_str(), NULL, 0));
-}
-
-// This function resolves symbol address within library specified by handle,
-// and returns it's address, in case of error function returns NULL.
-void* DLSym(DLHandle handle, string_view name)
-{
- char str[LINE_MAX];
- if (name.size() >= sizeof(str))
- return {};
-
- name.copy(str, name.size());
- str[name.size()] = 0;
- return ::GetProcAddress((HMODULE)handle, str);
-}
-
-/// This function unloads previously loadded library, specified by handle.
-/// In case of error this function returns `false'.
-bool DLClose(DLHandle handle)
-{
- return ::FreeLibrary(reinterpret_cast<HMODULE>(handle));
-}
-
-} // ::netcoredbg
-#endif
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file filesystem_win32.cpp
-/// This file contains definitions of windows-specific functions related to file system.
-
-#ifdef WIN32
-#include <windows.h>
-#include <string>
-#include "utils/filesystem.h"
-#include "utils/limits.h"
-
-
-namespace netcoredbg
-{
-
-const char* FileSystemTraits<Win32PlatformTag>::PathSeparatorSymbols = "/\\";
-
-// Function returns absolute path to currently running executable.
-std::string GetExeAbsPath()
-{
- const size_t MAX_LONGPATH = 1024;
- char hostPath[MAX_LONGPATH + 1];
- static const std::string result(hostPath, ::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH));
- return result;
-}
-
-
-// Function returns path to directory, which should be used for creation of
-// temporary files. Typically this is `/tmp` on Unix and something like
-// `C:\Users\localuser\Appdata\Local\Temp` on Windows.
-string_view GetTempDir()
-{
- CHAR path[MAX_PATH + 1];
- static const std::string result(path, GetTempPathA(MAX_PATH, path));
- return result;
-}
-
-
-// Function changes current working directory. Return value is `false` in case of error.
-bool SetWorkDir(const std::string &path)
-{
- // In the ANSI version of this function, the name is limited to MAX_PATH characters.
- // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcurrentdirectory
- if (path.size() >= MAX_PATH)
- return false;
-
- return SetCurrentDirectoryA(path.c_str());
-}
-
-} // ::netcoredbg
-#endif
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file filesystem_win32.h This file contains windows-specific details to FileSystem class.
-
-#ifdef WIN32
-#pragma once
-#include <cstddef>
-#include <windows.h>
-
-namespace netcoredbg
-{
- template <> struct FileSystemTraits<Win32PlatformTag>
- {
- const static size_t PathMax = MAX_PATH;
- const static size_t NameMax = MAX_PATH - 1; // not include terminal null.
- const static char PathSeparator = '\\';
- const static char* PathSeparatorSymbols;
- };
-}
-#endif
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file interop_win32.h This file contains windows-specific functions for Interop class defined in interop.h
-
-#ifdef WIN32
-#include <windows.h>
-#include <stddef.h>
-#include <string.h>
-#include <string>
-#include <set>
-
-#include "managed/interop.h"
-#include "utils/filesystem.h"
-#include "utils/limits.h"
-
-
-namespace netcoredbg
-{
-
-// This function searches *.dll files in specified directory and adds full paths to files
-// to semicolon-separated list `tpaList'.
-template <>
-void InteropTraits<Win32PlatformTag>::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList)
-{
- const char * const tpaExtensions[] = {
- "*.ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
- "*.dll",
- "*.ni.exe",
- "*.exe",
- };
-
- std::set<std::string> addedAssemblies;
-
- // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
- // then files with .dll extension, etc.
- for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
- {
- const char* ext = tpaExtensions[extIndex];
- size_t extLength = strlen(ext);
-
- std::string assemblyPath(directory);
- assemblyPath += FileSystem::PathSeparator;
- assemblyPath.append(tpaExtensions[extIndex]);
-
- WIN32_FIND_DATAA data;
- HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
-
- if (findHandle != INVALID_HANDLE_VALUE)
- {
- do
- {
- if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- {
-
- std::string filename(data.cFileName);
- size_t extPos = filename.length() - extLength;
- std::string filenameWithoutExt(filename.substr(0, extPos));
-
- // Make sure if we have an assembly with multiple extensions present,
- // we insert only one version of it.
- if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
- {
- addedAssemblies.insert(filenameWithoutExt);
-
- tpaList.append(directory);
- tpaList += FileSystem::PathSeparator;
- tpaList.append(filename);
- tpaList.append(";");
- }
- }
- }
- while (0 != FindNextFileA(findHandle, &data));
-
- FindClose(findHandle);
- }
- }
-}
-
-// This function unsets `CORECLR_ENABLE_PROFILING' environment variable.
-template <>
-void InteropTraits<Win32PlatformTag>::UnsetCoreCLREnv()
-{
- _putenv("CORECLR_ENABLE_PROFILING=");
-}
-
-// Returns the length of a BSTR.
-template <>
-UINT InteropTraits<Win32PlatformTag>::SysStringLen(BSTR bstrString)
-{
- return ::SysStringLen(bstrString);
-}
-
-} // ::netcoredbg
-#endif // WIN32
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file iosystem_win32.cpp This file contains windows-specific definitions of
-/// IOSystem class members (see iosystem.h).
-
-#ifdef WIN32
-#include <io.h>
-#include <fcntl.h>
-#include <ws2tcpip.h>
-#include <afunix.h>
-#include <stdexcept>
-#include <new>
-#include <memory>
-#include <atomic>
-#include <string.h>
-#include <assert.h>
-#include "utils/iosystem.h"
-#include "utils/limits.h"
-
-// short alias for full class name
-namespace { typedef netcoredbg::IOSystemTraits<netcoredbg::Win32PlatformTag> Class; }
-
-namespace
-{
- class Win32Exception : public std::runtime_error
- {
- struct Msg
- {
- mutable char buf[2 * LINE_MAX];
- };
-
- static const char* getmsg(const char *prefix, DWORD error, const Msg& msg = Msg())
- {
- int len = prefix ? snprintf(msg.buf, sizeof(msg.buf), "%s: ", prefix) : 0;
-
- if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- msg.buf + len, sizeof(msg.buf) - len, NULL))
- {
- return msg.buf;
- }
-
- snprintf(msg.buf + len, sizeof(msg.buf) - len, "error %#x", error);
- return msg.buf;
- }
-
- public:
- /// Specify Win32 error code and, optionally, error message prefix.
- Win32Exception(DWORD error, const char* prefix = nullptr) : std::runtime_error(getmsg(prefix, error)) {}
-
- /// Specify error message prefix (optionally). Win32 error code will be obtained via call to GetLastError().
- Win32Exception(const char *prefix = nullptr) : Win32Exception(prefix, GetLastError()) {}
-
- /// Specify explicitly error message prefix and error code.
- Win32Exception(const char *prefix, DWORD error) : Win32Exception(error, prefix) {}
- };
-
- struct Initializer
- {
- Initializer()
- {
- WSADATA wsa;
- int wsa_error = WSAStartup(MAKEWORD(2, 2), &wsa);
- if (wsa_error != 0)
- throw Win32Exception("WSAStartup failed", wsa_error);
- }
-
- ~Initializer()
- {
- WSACleanup();
- }
- };
-
- static Initializer initializer;
-
-#if 0
- // assuming domain=AF_UNIX, type=SOCK_STREAM, protocol=0
- int wsa_socketpair(int domain, int type, int protocol, SOCKET sv[2])
- {
-
- SOCKET serv = ::socket(domain, type, protocol);
- if (serv == INVALID_SOCKET)
- throw Win32Exception("can't create socket", WSAGetLastError());
-
- // TODO
- char name[] = "netcoredbg";
- size_t namelen = sizeof(name)-1;
-
- SOCKADDR_UN sa;
- sa.sun_family = domain;
- assert(namelen <= sizeof(sa.sun_path));
- memcpy(sa.sun_path, name, namelen);
- if (::bind(serv, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
- {
- auto err = WSAGetLastError();
- ::closesocket(serv);
- throw Win32Exception("can't bind socket", err);
- }
-
- u_long mode = 1;
- if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR)
- {
- auto err = WSAGetLastError();
- ::closesocket(serv);
- throw Win32Exception("ioctlsocket(FIONBIO)", err);
- }
-
- if (::listen(serv, 1) == SOCKET_ERROR && WSAGetLastError() != WSAEINPROGRESS)
- {
- auto err = WSAGetLastError();
- ::closesocket(serv);
- throw Win32Exception("ioctlsocket(FIONBIO)", err);
- }
-
- SOCKET conn = ::socket(domain, type, protocol);
- if (conn == INVALID_SOCKET)
- {
- auto err = WSAGetLastError();
- ::closesocket(serv);
- throw Win32Exception("can't create socket", err);
- }
-
- sa.sun_family = domain;
- memcpy(sa.sun_path, name, namelen);
- if (::connect(conn, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
- {
- auto err = WSAGetLastError();
- ::closesocket(serv);
- ::closesocket(conn);
- throw Win32Exception("can't bind socket", err);
- }
-
- mode = 0;
- if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR)
- {
- auto err = WSAGetLastError();
- ::closesocket(serv);
- ::closesocket(conn);
- throw Win32Exception("ioctlsocket(FIONBIO)", err);
- }
-
- SOCKET newsock = ::accept(serv, NULL, NULL);
- if (newsock == INVALID_SOCKET)
- {
- auto err = WSAGetLastError();
- ::closesocket(serv);
- ::closesocket(conn);
- throw Win32Exception("accept on socket", err);
- }
-
- ::closesocket(serv);
-
- sv[0] = newsock, sv[1] = conn;
- return 0;
- }
-#endif
-}
-
-
-// Function should create unnamed pipe and return two file handles
-// (reading and writing pipe ends) or return empty file handles if pipe can't be created.
-std::pair<Class::FileHandle, Class::FileHandle> Class::unnamed_pipe()
-{
-#if 0
- SOCKET sv[2];
- if (wsa_socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0)
- return {FileHandle(), FileHandle()};
-#endif
-
- static const size_t PipeSize = 32 * LINE_MAX;
-
- SECURITY_ATTRIBUTES saAttr;
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- saAttr.lpSecurityDescriptor = NULL;
-
- HANDLE reading_fd, writing_fd;
-
- static std::atomic<long> pipe_num;
- char pipe_name[MAX_PATH + 1];
- snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\Pipe\\Win32Pipes.%08x.%08x",
- GetCurrentProcessId(), pipe_num++);
-
- reading_fd = CreateNamedPipeA(pipe_name,
- PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
- PIPE_TYPE_BYTE | PIPE_WAIT,
- 1, // number of pipes
- PipeSize, PipeSize,
- 0, // 50ms default timeout
- &saAttr);
-
- if (reading_fd == INVALID_HANDLE_VALUE)
- {
- perror("CreateNamedPipeA");
- return { FileHandle(), FileHandle() };
- }
-
- writing_fd = CreateFileA(pipe_name,
- GENERIC_WRITE,
- 0, // no sharing
- &saAttr,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
- NULL);
-
- if (writing_fd == INVALID_HANDLE_VALUE)
- {
- auto err = GetLastError();
- ::CloseHandle(writing_fd);
- fprintf(stderr, "CreateFile pipe error: %#x\n", err);
- return { FileHandle(), FileHandle() };
- }
-
- if (!SetHandleInformation(writing_fd, HANDLE_FLAG_INHERIT, 0))
- {
- fprintf(stderr, "SetHandleInformation failed!\n");
- return { FileHandle(), FileHandle() };
- }
-
- if (!SetHandleInformation(reading_fd, HANDLE_FLAG_INHERIT, 0))
- {
- fprintf(stderr, "SetHandleInformation failed!\n");
- return { FileHandle(), FileHandle() };
- }
-
- return { FileHandle(reading_fd), FileHandle(writing_fd) };
-}
-
-
-// Function creates listening TCP socket on given port, waits, accepts single
-// connection, and return file descriptor related to the accepted connection.
-// In case of error, empty file handle will be returned.
-Class::FileHandle Class::listen_socket(unsigned port)
-{
- assert(port > 0 && port < 65536);
-
- SOCKET newsockfd;
- int clilen;
- struct sockaddr_in serv_addr, cli_addr;
-
- SOCKET sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
- if (sockFd == INVALID_SOCKET)
- {
- fprintf(stderr, "can't create socket: %#x\n", WSAGetLastError());
- return {};
- }
-
- BOOL enable = 1;
- if (::setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, (const char *)&enable, sizeof(BOOL)) == SOCKET_ERROR)
- {
- ::closesocket(sockFd);
- fprintf(stderr, "setsockopt failed\n");
- return {};
- }
- memset(&serv_addr, 0, sizeof(serv_addr));
-
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_addr.s_addr = INADDR_ANY;
- serv_addr.sin_port = htons(port);
-
- if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
- {
- ::closesocket(sockFd);
- fprintf(stderr, "can't bind to specified port!\n");
- return {};
- }
-
- ::listen(sockFd, 1);
-
- clilen = sizeof(cli_addr);
- newsockfd = ::accept(sockFd, (struct sockaddr*)&cli_addr, &clilen);
- ::closesocket(sockFd);
- if (newsockfd == INVALID_SOCKET)
- {
- fprintf(stderr, "can't accept connection\n");
- return {};
- }
-
- return FileHandle(newsockfd);
-}
-
-// Function enables or disables inheritance of file handle for child processes.
-Class::IOResult Class::set_inherit(const FileHandle& fh, bool inherit)
-{
- DWORD flags;
- if (!GetHandleInformation(fh.handle, &flags))
- return {IOResult::Error};
-
- if (inherit)
- flags |= HANDLE_FLAG_INHERIT;
- else
- flags &= ~HANDLE_FLAG_INHERIT;
-
- if (!SetHandleInformation(fh.handle, HANDLE_FLAG_INHERIT, flags))
- return {IOResult::Error};
-
- return {IOResult::Success};
-}
-
-// Function perform reading from the file: it may read up to `count' bytes to `buf'.
-Class::IOResult Class::read(const FileHandle& fh, void *buf, size_t count)
-{
- DWORD dwRead = 0;
- OVERLAPPED ov = {};
- if (! ReadFile(fh.handle, buf, (DWORD)count, &dwRead, &ov))
- return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwRead };
- else
- return { (dwRead == 0 ? IOResult::Eof : IOResult::Success), dwRead };
-}
-
-
-// Function perform writing to the file: it may write up to `count' byte from `buf'.
-Class::IOResult Class::write(const FileHandle& fh, const void *buf, size_t count)
-{
- // see https://stackoverflow.com/questions/43939424/writefile-with-windows-sockets-returns-invalid-parameter-error
- DWORD dwWritten = 0;
- OVERLAPPED ov = {};
- if (! WriteFile(fh.handle, buf, (DWORD)count, &dwWritten, &ov))
- return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwWritten };
- else
- return { IOResult::Success, dwWritten };
-}
-
-
-Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count)
-{
- if (fh.handle == INVALID_HANDLE_VALUE)
- return {};
-
- AsyncHandle result;
- result.check_eof = true;
- result.handle = fh.handle;
- result.overlapped.reset(new OVERLAPPED);
- memset(result.overlapped.get(), 0, sizeof(OVERLAPPED));
- result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (result.overlapped->hEvent == INVALID_HANDLE_VALUE)
- return {};
-
- DWORD val;
- DWORD bytesRead;
-
- if (GetConsoleMode(fh.handle, &val))
- { // file handle is the console
- // first, remove all events before the first key event, if exists
- while (GetNumberOfConsoleInputEvents(fh.handle, &val) && val)
- {
- INPUT_RECORD event;
- if (!PeekConsoleInput(fh.handle, &event, 1, &bytesRead))
- return {};
- if (event.EventType != KEY_EVENT || (event.EventType == KEY_EVENT && !event.Event.KeyEvent.bKeyDown))
- {
- if (!ReadConsoleInput(fh.handle, &event, 1, &bytesRead))
- return {};
- }
- else
- break;
- }
- if (!val)
- {
- // nothing to read from the console -- defer call to ReadFile
- result.buf = buf, result.count = count;
- return result;
- }
- }
-
- if (! ReadFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get()))
- {
- if (GetLastError() != ERROR_IO_PENDING)
- return {};
- }
-
- return result;
-}
-
-Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count)
-{
- if (fh.handle == INVALID_HANDLE_VALUE)
- return {};
-
- AsyncHandle result;
- result.handle = fh.handle;
- result.overlapped.reset(new OVERLAPPED);
- memset(result.overlapped.get(), 0, sizeof(OVERLAPPED));
- result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (result.overlapped->hEvent == INVALID_HANDLE_VALUE)
- return {};
-
- if (! WriteFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get()))
- {
- if (GetLastError() != ERROR_IO_PENDING)
- return {};
- }
-
- return result;
-}
-
-bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout)
-{
- // console workaround
- for (auto it = begin; it != end; ++it)
- {
- if (it->handle.buf)
- {
- DWORD val;
- if (GetNumberOfConsoleInputEvents(it->handle.handle, &val) && val)
- SetEvent(it->handle.overlapped->hEvent);
- }
- }
-
- // count number of active handles
- unsigned count = 0;
- for (auto it = begin; it != end; ++it)
- if (*it) ++count;
-
- // allocate memory for events array
- HANDLE *events = static_cast<HANDLE*>(alloca(count * sizeof(HANDLE)));
- unsigned n = 0;
- for (auto it = begin; it != end; ++it)
- {
- if (*it)
- events[n++] = it->handle.overlapped->hEvent;
- }
-
- assert(n == count);
- DWORD result = WaitForMultipleObjects(count, events, FALSE, DWORD(timeout.count()));
- return result != WAIT_FAILED && result != WAIT_TIMEOUT;
-}
-
-Class::IOResult Class::async_cancel(AsyncHandle& h)
-{
- if (!h)
- return {IOResult::Error};
-
- if (!CloseHandle(h.overlapped->hEvent))
- perror("CloseHandle(event) error");
-
- // console workaround -- canceling deffered operation
- if (h.buf)
- {
- h = AsyncHandle();
- return {IOResult::Success};
- }
-
- IOResult result;
- if (!CancelIoEx(h.handle, h.overlapped.get()))
- result = {IOResult::Error};
- else
- result = {IOResult::Success};
-
- h = AsyncHandle();
- return result;
-}
-
-Class::IOResult Class::async_result(AsyncHandle& h)
-{
- if (!h)
- return {IOResult::Error};
-
- DWORD bytes = 1;
- bool finished;
-
- if (h.buf)
- {
- // workaround for the console
- finished = true;
- }
- else
- {
- // pipes, normal files, etc...
- finished = GetOverlappedResult(h.handle, h.overlapped.get(), &bytes, FALSE);
- if (!finished)
- {
- DWORD error = GetLastError();
- if (error == ERROR_IO_INCOMPLETE)
- return {IOResult::Pending};
- }
- }
-
- if (!CloseHandle(h.overlapped->hEvent))
- perror("CloseHandle(event) error");
-
- bool check_eof = h.check_eof;
-
- h = AsyncHandle();
-
- if (!finished)
- return {IOResult::Error};
-
- if (check_eof && bytes == 0)
- return {IOResult::Eof, bytes};
-
- return {IOResult::Success, bytes};
-}
-
-// Function closes the file represented by file handle.
-Class::IOResult Class::close(const FileHandle& fh)
-{
- assert(fh);
- if (fh.type == FileHandle::Socket)
- return { ::closesocket((SOCKET)fh.handle) == 0 ? IOResult::Success : IOResult::Error };
- else
- return { ::CloseHandle(fh.handle) ? IOResult::Success : IOResult::Error };
-}
-
-
-// Function allows non-blocking IO on files, it is similar with select(2) system call on Unix.
-// Arguments includes: pointers to three sets of file handles (for reading, for writing, and for
-// exceptions), and timeout value, in milliseconds. Any pointer might have NULL value if some set
-// isn't specified.
-// Function returns -1 on error, 0 on timeout or number of ready to read/write file handles.
-// If function returns value greater than zero, at least one of the sets, passed in arguments,
-// is not empty and contains file handles ready to read/write/etc...
-
-
-// This function returns triplet of currently selected standard files.
-Class::IOSystem::StdFiles Class::get_std_files()
-{
- using Handles = std::tuple<IOSystem::FileHandle, IOSystem::FileHandle, IOSystem::FileHandle>;
- /*thread_local*/ static alignas(alignof(Handles)) char mem[sizeof(Handles)]; // TODO
- Handles& handles = *new (mem) Handles {
- FileHandle(GetStdHandle(STD_INPUT_HANDLE)),
- FileHandle(GetStdHandle(STD_OUTPUT_HANDLE)),
- FileHandle(GetStdHandle(STD_ERROR_HANDLE))
- };
- return { std::get<IOSystem::Stdin>(handles),
- std::get<IOSystem::Stdout>(handles),
- std::get<IOSystem::Stderr>(handles) };
-}
-
-
-// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor.
-// Substitution exists only during life time of StsIOSwap instance.
-Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true)
-{
- const static unsigned NFD = std::tuple_size<StdFiles>::value;
- static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
- static const int open_flags[NFD] = {_O_RDONLY | _O_BINARY, _O_BINARY, _O_BINARY};
- const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)};
-
- const FileHandle new_handles[NFD] = {
- std::get<IOSystem::Stdin>(files).handle,
- std::get<IOSystem::Stdout>(files).handle,
- std::get<IOSystem::Stderr>(files).handle };
-
- fflush(stdout);
- fflush(stderr);
-
- for (unsigned n = 0; n < NFD; n++)
- {
- if (new_handles[n].type != FileHandle::FileOrPipe)
- throw std::runtime_error("can't use socket handle for stdin/stdout/stderr");
- }
-
- for (unsigned n = 0; n < NFD; n++)
- {
- m_orig_handle[n] = GetStdHandle(std_handles[n]);
- if (m_orig_handle[n] == INVALID_HANDLE_VALUE)
- {
- char msg[256];
- snprintf(msg, sizeof(msg), "GetStdHandle(%#x): error", std_handles[n]);
- throw std::runtime_error(msg);
- }
-
- if (!SetHandleInformation(new_handles[n].handle, HANDLE_FLAG_INHERIT, 1))
- fprintf(stderr, "SetHandleInformation failed!\n");
-
- if (!SetStdHandle(std_handles[n], new_handles[n].handle))
- {
- char msg[256];
- snprintf(msg, sizeof(msg), "SetStdHandle(%#x, %p): error", std_handles[n], new_handles[n].handle);
- throw std::runtime_error(msg);
- }
-
-
- int fd = _open_osfhandle(reinterpret_cast<intptr_t>(new_handles[n].handle), open_flags[n]);
- if (fd == -1)
- throw Win32Exception("_open_osfhandle");
-
- m_orig_fd[n] = _dup(open_fds[n]);
- if (m_orig_fd[n] == -1)
- throw Win32Exception("_dup");
-
- if (_dup2(fd, open_fds[n]) == -1)
- throw Win32Exception("_dup2");
-
- close(fd);
- }
-}
-
-Class::StdIOSwap::~StdIOSwap()
-{
- if (!m_valid)
- return;
-
- const static unsigned NFD = std::tuple_size<StdFiles>::value;
- static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
- const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)};
-
- fflush(stdout);
- fflush(stderr);
-
- for (unsigned n = 0; n < NFD; n++)
- {
- if (!SetStdHandle(std_handles[n], m_orig_handle[n]))
- {
- abort();
- }
-
- _dup2(m_orig_fd[n], open_fds[n]);
- close(m_orig_fd[n]);
- }
-}
-
-#endif // WIN32
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file iosystem_win32.h This file contains windows-specific declaration of IOSystem class (see iosystem.h).
-//
-#ifdef _WIN32
-#pragma once
-#include <winsock2.h>
-#include <windows.h> // TODO
-#include <assert.h>
-#include <tuple>
-
-namespace netcoredbg
-{
-
-template <> struct IOSystemTraits<Win32PlatformTag>
-{
- struct FileHandle
- {
- FileHandle() : handle(INVALID_HANDLE_VALUE), type(FileOrPipe) {}
-
- explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; }
-
- enum FileType
- {
- FileOrPipe,
- Socket
- };
-
- FileHandle(HANDLE filefd) : handle(filefd), type(FileOrPipe) {}
- FileHandle(SOCKET sockfd) : handle((HANDLE)sockfd), type(Socket) {}
-
- HANDLE handle;
- enum FileType type;
- };
-
- struct AsyncHandle
- {
- HANDLE handle;
- std::unique_ptr<OVERLAPPED> overlapped;
- bool check_eof;
-
- // workaround: non-blocking reading from console
- void *buf;
- size_t count;
-
- AsyncHandle()
- : handle(INVALID_HANDLE_VALUE), overlapped(), check_eof(false), buf(nullptr), count(0)
- {}
-
- AsyncHandle(AsyncHandle&& other) noexcept
- : handle(other.handle), overlapped(std::move(other.overlapped)), check_eof(other.check_eof),
- buf(other.buf), count(other.count)
- {
- other.handle = INVALID_HANDLE_VALUE;
- }
-
- AsyncHandle& operator=(AsyncHandle&& other) noexcept
- {
- return this->~AsyncHandle(), *new (this) AsyncHandle(std::move(other));
- }
-
- explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; }
- };
-
- using IOSystem = typename IOSystemImpl<IOSystemTraits<Win32PlatformTag> >;
- using IOResult = IOSystem::IOResult;
-
- static std::pair<FileHandle, FileHandle> unnamed_pipe();
- static FileHandle listen_socket(unsigned tcp_port);
- static IOResult set_inherit(const FileHandle &, bool);
- static IOResult read(const FileHandle &, void *buf, size_t count);
- static IOResult write(const FileHandle &, const void *buf, size_t count);
- static AsyncHandle async_read(const FileHandle &, void *buf, size_t count);
- static AsyncHandle async_write(const FileHandle &, const void *buf, size_t count);
- static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds);
- static IOResult async_cancel(AsyncHandle &);
- static IOResult async_result(AsyncHandle &);
- static IOResult close(const FileHandle &);
-
- static IOSystem::StdFiles get_std_files();
-
- struct StdIOSwap
- {
- using StdFiles = IOSystem::StdFiles;
- using StdFileType = IOSystem::StdFileType;
- StdIOSwap(const StdFiles &);
- ~StdIOSwap();
-
- StdIOSwap(StdIOSwap&& other)
- {
- m_valid = other.m_valid;
- if (!m_valid)
- return;
-
- other.m_valid = false;
- for (unsigned n = 0; n < std::tuple_size<StdFiles>::value; n++)
- {
- m_orig_handle[n] = other.m_orig_handle[n];
- m_orig_fd[n] = other.m_orig_fd[n];
- }
- }
-
- bool m_valid;
- HANDLE m_orig_handle[std::tuple_size<StdFiles>::value];
- int m_orig_fd[std::tuple_size<StdFiles>::value];
- };
-};
-
-} // ::netcoredbg
-#endif // WIN32
+++ /dev/null
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file platform_win32.cpp This file contains windows-specific function definitions,
-/// for functions defined in platform.h
-
-#ifdef WIN32
-#include <windows.h>
-#include <stdlib.h> // char **environ
-#include "utils/platform.h"
-#include "utils/limits.h"
-
-namespace netcoredbg
-{
-
-// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix).
-unsigned long OSPageSize()
-{
- static unsigned long pageSize = []{
- SYSTEM_INFO si;
- GetSystemInfo(&si);
- return si.dwPageSize;
- }();
-
- return pageSize;
-}
-
-
-// Function suspends process execution for specified amount of time (in microseconds)
-void USleep(unsigned long usec)
-{
- HANDLE timer;
- LARGE_INTEGER ft;
-
- ft.QuadPart = -(10*(long)usec); // Convert to 100 nanosecond interval, negative value indicates relative time
-
- timer = CreateWaitableTimer(NULL, TRUE, NULL);
- SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
- WaitForSingleObject(timer, INFINITE);
- CloseHandle(timer);
-}
-
-
-// Function returns list of environment variables (like char **environ).
-char** GetSystemEnvironment()
-{
- return environ;
-}
-
-} // ::netcoredbg
-#endif