combined updates

This commit is contained in:
James E McClure committed 2019-03-22 21:33:05 -04:00
commit 23e221d84e
50 files changed
+5065 -4667

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
#ifndef included_StackTraceErrorHandlers
#define included_StackTraceErrorHandlers
#include "StackTrace/StackTrace.h"
#include <functional>
#include "mpi.h"
namespace StackTrace
{
/*!
* Set the error handler
* @param[in] abort Function to terminate the program: abort(msg,type)
*/
void setErrorHandler( std::function<void( const StackTrace::abort_error& )> abort );
//! Clear the error handler
void clearErrorHandler();
//! Set an error handler for MPI
void setMPIErrorHandler( MPI_Comm comm );
//! Clear an error handler for MPI
void clearMPIErrorHandler( MPI_Comm comm );
//! Initialize globalCallStack functionallity
void globalCallStackInitialize( MPI_Comm comm );
//! Clean up globalCallStack functionallity
void globalCallStackFinalize();
} // namespace StackTrace
#endif
+4
View File
@@ -0,0 +1,4 @@
This directory contains code external code released with permission under the license of this project.
Original code and license are availible at:
https://bitbucket.org/mberrill/StackTrace
File diff suppressed because it is too large. Load diff
+310
View File
@@ -0,0 +1,310 @@
/*
Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef included_StackTrace
#define included_StackTrace
#include <array>
#include <functional>
#include <iostream>
#include <set>
#include <thread>
#include <vector>
#include "StackTrace/string_view.h"
namespace StackTrace {
//! Class to contain stack trace info for a single thread/process
struct stack_info {
uint32_t line;
void *address;
void *address2;
std::array<char, 56> object;
std::array<char, 48> objectPath;
std::array<char, 64> filename;
std::array<char, 64> filenamePath;
std::array<char, 256> function;
//! Default constructor
stack_info();
//! Reset the stack
void clear();
//! Operator==
bool operator==( const stack_info &rhs ) const;
//! Operator!=
bool operator!=( const stack_info &rhs ) const;
//! Get the minimum width to print the addresses
int getAddressWidth() const;
//! Print the stack info
std::string print( int widthAddress = 16, int widthObject = 20, int widthFunction = 32 ) const;
//! Print the stack info
static void print( std::ostream &out, const std::vector<stack_info> &stack,
const StackTrace::string_view &prefix = "" );
//! Print the stack info
void print2(
char *txt, int widthAddress = 16, int widthObject = 20, int widthFunction = 32 ) const;
//! Compute the number of bytes needed to store the object
size_t size() const;
//! Pack the data to a byte array, returning a pointer to the end of the data
char *pack( char *ptr ) const;
//! Unpack the data from a byte array, returning a pointer to the end of the data
const char *unpack( const char *ptr );
};
//! Class to contain stack trace info for multiple threads/processes
struct multi_stack_info {
int N; // Number of threads/processes
stack_info stack; // Current stack item
std::vector<multi_stack_info> children; // Children
//! Default constructor
multi_stack_info() : N( 0 ) {}
//! Construct from a simple call stack
explicit multi_stack_info( const std::vector<stack_info> & );
//! Copy constructor from a simple call stack
multi_stack_info &operator=( const std::vector<stack_info> & );
//! Reset the stack
void clear();
//! Is the stack empty
bool empty() const { return N == 0; }
//! Add the given stack to the multistack
void add( size_t len, const stack_info *stack );
//! Add the given stack to the multistack
void add( const multi_stack_info &stack );
//! Compute the number of bytes needed to store the object
size_t size() const;
//! Pack the data to a byte array, returning a pointer to the end of the data
char *pack( char *ptr ) const;
//! Unpack the data from a byte array, returning a pointer to the end of the data
const char *unpack( const char *ptr );
//! Print the stack info
std::vector<std::string> print( const StackTrace::string_view &prefix = "" ) const;
//! Print the stack info
void print( std::ostream &out, const StackTrace::string_view &prefix = "" ) const;
//! Print the stack info
std::string printString( const StackTrace::string_view &prefix = "" ) const;
private:
template<class FUN>
void print2( int Np, char *prefix, int w[3], bool c, FUN &fun ) const;
int getAddressWidth() const;
int getObjectWidth() const;
int getFunctionWidth() const;
};
//!< Terminate type
enum class terminateType : uint8_t { signal, exception, abort, MPI, unknown };
enum class printStackType : uint8_t { local = 1, threaded = 2, global = 3 };
//!< Class to contain exception info from abort
class abort_error : public std::exception
{
public:
std::string message; //!< Abort message
std::string filename; //!< File where abort was called
terminateType type; //!< What caused the termination
printStackType stackType; //!< Print the local stack, all threads, or global call stack
uint8_t signal; //!< Signal number
int line; //!< Line number where abort was called
size_t bytes; //!< Memory in use during abort
std::vector<void *> stack; //!< Local call stack for abort
public:
virtual const char *what() const noexcept override;
abort_error();
virtual ~abort_error() {}
private:
mutable std::string d_msg;
};
//!< Class to contain symbol information
struct symbols_struct {
char type;
void *address;
std::array<char, 56> obj;
std::array<char, 56> objPath;
};
/*!
* @brief Get the current call stack
* @details This function returns the current call stack for the current thread
* @return Returns vector containing the stack
*/
std::vector<stack_info> getCallStack();
/*!
* @brief Get the current call stack for a thread
* @details This function returns the current call stack for the given thread
* @param[in] id The thread id of the stack we want to return
* @return Returns vector containing the stack
*/
std::vector<stack_info> getCallStack( std::thread::native_handle_type id );
/*!
* @brief Get the current call stack for all threads
* @details This function returns the current call stack for all threads
* in the current process.
* Note: This functionality may not be availible on all platforms
* @return Returns vector containing the stack
*/
multi_stack_info getAllCallStacks();
/*!
* @brief Get the current call stack for all threads/processes
* @details This function returns the current call stack for all threads
* for all processes in the current process. This function requires
* the user to call globalCallStackInitialize() before calling this
* routine, and globalCallStackFinalize() before exiting.
* Note: This functionality may not be availible on all platforms
* @return Returns vector containing the stack
*/
multi_stack_info getGlobalCallStacks();
/*!
* @brief Clean up the stack trace
* @details This function modifies the stack trace to remove entries
* related to acquiring the stack trace in an attempt to make it
* more useful for display/users.
* @param[in,out] stack The stack trace to modify
*/
void cleanupStackTrace( multi_stack_info &stack );
//! Function to return the current call stack for the current thread
std::vector<void *> backtrace();
//! Function to return the current call stack for the given thread
std::vector<void *> backtrace( std::thread::native_handle_type id );
//! Function to return the current call stack for all threads
std::vector<std::vector<void *>> backtraceAll();
//! Function to return the stack info for a given address
stack_info getStackInfo( void *address );
//! Function to return the stack info for a given address
std::vector<stack_info> getStackInfo( const std::vector<void *> &address );
//! Function to return the signal name
const char *signalName( int signal );
/*!
* Return the symbols from the current executable (not availible for all platforms)
* @return Returns the symbols loaded
*/
std::vector<symbols_struct> getSymbols();
//! Clear internal symbol data
void clearSymbols();
/*!
* Return the name of the executable
* @return Returns the name of the executable (usually the full path)
*/
std::string getExecutable();
/*!
* Return the search path for the symbols
* @return Returns the search path for the symbols
*/
std::string getSymPaths();
/*!
* Set the given signals to the handler
* @param[in] signals Signals to handle
* @param[in] handler Function to terminate the program: abort(signal)
*/
void setSignals( const std::vector<int> &signals, void ( *handler )( int ) );
//! Clear a signal set by setSignals
void clearSignal( int signal );
//! Clear a signal set by setSignals
void clearSignals( const std::vector<int> &signals );
//! Clear all signals set by setSignals
void clearSignals();
//! Raise a signal
void raiseSignal( int signal );
//! Return a list of all signals that can be caught
std::vector<int> allSignalsToCatch();
//! Return a default list of signals to catch
std::vector<int> defaultSignalsToCatch();
//! Get a list of the active threads
std::vector<std::thread::native_handle_type> activeThreads();
//! Get a handle to this thread
std::thread::native_handle_type thisThread();
/*!
* @brief Call system command
* @details This function calls a system command, waits for the program
* to execute, captures and returns the output and exit code.
* @param[in] cmd Command to execute
* @param[out] exit_code Exit code returned from child process
* @return Returns string containing the output
*/
std::string exec( const string_view &cmd, int &exit_code );
/*!
* @brief Create stack from string
* @details This function creates the call stack from the string generated by print
* @param[in] str Vector of strings containing call stack
* @return Returns the call stack
*/
multi_stack_info generateFromString( const std::vector<std::string> &str );
/*!
* @brief Create stack from string
* @details This function creates the call stack from the string
* @param[in] str String containing call stack
* @return Returns the call stack
*/
multi_stack_info generateFromString( const std::string &str );
} // namespace StackTrace
#endif
+296
View File
@@ -0,0 +1,296 @@
#define NOMINMAX
#include "StackTrace/Utilities.h"
#include "StackTrace/ErrorHandlers.h"
#include "StackTrace/StackTrace.h"
#include <algorithm>
#include <csignal>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#ifdef USE_MPI
#include "mpi.h"
#endif
#ifdef USE_TIMER
#include "MemoryApp.h"
#endif
#define perr std::cerr
// Detect the OS
// clang-format off
#if defined( WIN32 ) || defined( _WIN32 ) || defined( WIN64 ) || defined( _WIN64 ) || defined( _MSC_VER )
#define USE_WINDOWS
#elif defined( __APPLE__ )
#define USE_MAC
#elif defined( __linux ) || defined( __linux__ ) || defined( __unix ) || defined( __posix )
#define USE_LINUX
#define USE_NM
#else
#error Unknown OS
#endif
// clang-format on
// Include system dependent headers
// clang-format off
#ifdef USE_WINDOWS
#include <process.h>
#include <psapi.h>
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#else
#include <dlfcn.h>
#include <execinfo.h>
#include <sched.h>
#include <sys/time.h>
#include <ctime>
#include <unistd.h>
#endif
#ifdef USE_LINUX
#include <malloc.h>
#endif
#ifdef USE_MAC
#include <mach/mach.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#endif
// clang-format on
namespace StackTrace {
/****************************************************************************
* Function to find an entry *
****************************************************************************/
template<class TYPE>
inline size_t findfirst( const std::vector<TYPE> &X, TYPE Y )
{
if ( X.empty() )
return 0;
size_t lower = 0;
size_t upper = X.size() - 1;
if ( X[lower] >= Y )
return lower;
if ( X[upper] < Y )
return upper;
while ( ( upper - lower ) != 1 ) {
size_t value = ( upper + lower ) / 2;
if ( X[value] >= Y )
upper = value;
else
lower = value;
}
return upper;
}
/****************************************************************************
* Function to terminate the program *
****************************************************************************/
static bool abort_throwException = false;
static printStackType abort_stackType = printStackType::global;
static int force_exit = 0;
void Utilities::setAbortBehavior( bool throwException, int stackType )
{
abort_throwException = throwException;
abort_stackType = static_cast<printStackType>( stackType );
}
void Utilities::abort( const std::string &message, const std::string &filename, const int line )
{
abort_error err;
err.message = message;
err.filename = filename;
err.type = terminateType::abort;
err.line = line;
err.bytes = Utilities::getMemoryUsage();
err.stackType = abort_stackType;
err.stack = StackTrace::backtrace();
throw err;
}
static void terminate( const StackTrace::abort_error &err )
{
clearErrorHandler();
// Print the message and abort
if ( force_exit > 1 ) {
std::abort();
} else if ( !abort_throwException ) {
// Use MPI_abort (will terminate all processes)
force_exit = 2;
perr << err.what();
#if defined( USE_MPI ) || defined( HAVE_MPI )
int initialized = 0, finalized = 0;
MPI_Initialized( &initialized );
MPI_Finalized( &finalized );
if ( initialized != 0 && finalized == 0 ) {
clearMPIErrorHandler( MPI_COMM_WORLD );
MPI_Abort( MPI_COMM_WORLD, -1 );
}
#endif
std::abort();
} else {
perr << err.what();
std::abort();
}
}
/****************************************************************************
* Functions to set the error handler *
****************************************************************************/
static void setTerminateErrorHandler()
{
// Set the terminate routine for runtime errors
StackTrace::setErrorHandler( terminate );
}
void Utilities::setErrorHandlers()
{
#ifdef USE_MPI
setMPIErrorHandler( MPI_COMM_WORLD );
setMPIErrorHandler( MPI_COMM_SELF );
#endif
setTerminateErrorHandler();
}
void Utilities::clearErrorHandlers()
{
#ifdef USE_MPI
clearMPIErrorHandler( MPI_COMM_WORLD );
clearMPIErrorHandler( MPI_COMM_SELF );
#endif
}
/****************************************************************************
* Function to get the memory usage *
* Note: this function should be thread-safe *
****************************************************************************/
// clang-format off
#if defined( USE_MAC ) || defined( USE_LINUX )
// Get the page size on mac or linux
static size_t page_size = static_cast<size_t>( sysconf( _SC_PAGESIZE ) );
#endif
size_t Utilities::getSystemMemory()
{
#if defined( USE_LINUX )
static long pages = sysconf( _SC_PHYS_PAGES );
size_t N_bytes = pages * page_size;
#elif defined( USE_MAC )
int mib[2] = { CTL_HW, HW_MEMSIZE };
u_int namelen = sizeof( mib ) / sizeof( mib[0] );
uint64_t size;
size_t len = sizeof( size );
size_t N_bytes = 0;
if ( sysctl( mib, namelen, &size, &len, nullptr, 0 ) == 0 )
N_bytes = size;
#elif defined( USE_WINDOWS )
MEMORYSTATUSEX status;
status.dwLength = sizeof( status );
GlobalMemoryStatusEx( &status );
size_t N_bytes = status.ullTotalPhys;
#else
#error Unknown OS
#endif
return N_bytes;
}
size_t Utilities::getMemoryUsage()
{
#ifdef USE_TIMER
size_t N_bytes = MemoryApp::getTotalMemoryUsage();
#else
#if defined( USE_LINUX )
struct mallinfo meminfo = mallinfo();
size_t size_hblkhd = static_cast<unsigned int>( meminfo.hblkhd );
size_t size_uordblks = static_cast<unsigned int>( meminfo.uordblks );
size_t N_bytes = size_hblkhd + size_uordblks;
#elif defined( USE_MAC )
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
if ( KERN_SUCCESS !=
task_info( mach_task_self(), TASK_BASIC_INFO, (task_info_t) &t_info, &t_info_count ) ) {
return 0;
}
size_t N_bytes = t_info.virtual_size;
#elif defined( USE_WINDOWS )
PROCESS_MEMORY_COUNTERS memCounter;
GetProcessMemoryInfo( GetCurrentProcess(), &memCounter, sizeof( memCounter ) );
size_t N_bytes = memCounter.WorkingSetSize;
#else
#error Unknown OS
#endif
#endif
return N_bytes;
}
// clang-format on
/****************************************************************************
* Functions to get the time and timer resolution *
****************************************************************************/
#if defined( USE_WINDOWS )
double Utilities::time()
{
LARGE_INTEGER end, f;
QueryPerformanceFrequency( &f );
QueryPerformanceCounter( &end );
double time = ( (double) end.QuadPart ) / ( (double) f.QuadPart );
return time;
}
double Utilities::tick()
{
LARGE_INTEGER f;
QueryPerformanceFrequency( &f );
double resolution = ( (double) 1.0 ) / ( (double) f.QuadPart );
return resolution;
}
#elif defined( USE_LINUX ) || defined( USE_MAC )
double Utilities::time()
{
timeval current_time;
gettimeofday( &current_time, nullptr );
double time = ( (double) current_time.tv_sec ) + 1e-6 * ( (double) current_time.tv_usec );
return time;
}
double Utilities::tick()
{
timeval start, end;
gettimeofday( &start, nullptr );
gettimeofday( &end, nullptr );
while ( end.tv_sec == start.tv_sec && end.tv_usec == start.tv_usec )
gettimeofday( &end, nullptr );
double resolution = ( (double) ( end.tv_sec - start.tv_sec ) ) +
1e-6 * ( (double) ( end.tv_usec - start.tv_usec ) );
return resolution;
}
#else
#error Unknown OS
#endif
/****************************************************************************
* Cause a segfault *
****************************************************************************/
void Utilities::cause_segfault()
{
int *ptr = nullptr;
ptr[0] = 0;
}
/****************************************************************************
* Call system command *
****************************************************************************/
std::string Utilities::exec( const string_view &cmd, int &exit_code )
{
return StackTrace::exec( cmd, exit_code );
}
} // namespace StackTrace
+99
View File
@@ -0,0 +1,99 @@
#ifndef included_StackTrace_Utilities
#define included_StackTrace_Utilities
#include <stdexcept>
#include <string>
#include <thread>
#include "StackTrace/StackTrace.h"
#include "StackTrace/string_view.h"
namespace StackTrace {
namespace Utilities {
/*!
* Aborts the run after printing an error message with file and
* line number information.
*/
void abort( const std::string &message, const std::string &filename, const int line );
/*!
* Set the behavior of abort
* @param throwException Throw an exception instead of MPI_Abort (default is false)
* @param stackType Type of stack to get (1: thread local stack, 2: all threads, 3: global)
*/
void setAbortBehavior( bool throwException, int stackType = 2 );
//! Function to set the error handlers
void setErrorHandlers();
//! Function to clear the error handlers
void clearErrorHandlers();
/*!
* Function to get the memory availible.
* This function will return the total memory availible
* Note: depending on the implimentation, this number may be rounded to
* to a multiple of the page size.
* If this function fails, it will return 0.
*/
size_t getSystemMemory();
/*!
* Function to get the memory usage.
* This function will return the total memory used by the application.
* Note: depending on the implimentation, this number may be rounded to
* to a multiple of the page size.
* If this function fails, it will return 0.
*/
size_t getMemoryUsage();
//! Function to get an arbitrary point in time
double time();
//! Function to get the resolution of time
double tick();
/*!
* Sleep for X ms
* @param N Time to sleep (ms)
*/
inline void sleep_ms( int N ) { std::this_thread::sleep_for( std::chrono::milliseconds( N ) ); }
/*!
* Sleep for X s
* @param N Time to sleep (s)
*/
inline void sleep_s( int N ) { std::this_thread::sleep_for( std::chrono::seconds( N ) ); }
//! Cause a segfault
void cause_segfault();
/*!
* @brief Call system command
* @details This function calls a system command, waits for the program
* to execute, captures and returns the output and exit code.
* @param[in] cmd Command to execute
* @param[out] exit_code Exit code returned from child process
* @return Returns string containing the output
*/
std::string exec( const StackTrace::string_view &cmd, int &exit_code );
} // namespace Utilities
} // namespace StackTrace
#endif
+193
View File
@@ -0,0 +1,193 @@
#ifndef included_StackTrace_stringView
#define included_StackTrace_stringView
#include <cstring>
#include <ostream>
namespace StackTrace {
// string_view
class string_view
{
public:
// Constants:
static constexpr size_t npos = size_t( -1 );
// Constructions
constexpr string_view() noexcept : d_data( nullptr ), d_size( 0 ) {}
constexpr string_view( string_view&& ) noexcept = default;
constexpr string_view( const string_view& ) noexcept = default;
constexpr string_view( const char* s ) : d_data( s ), d_size( s ? strlen( s ) : 0 ) {}
constexpr string_view( const char* s, size_t count ) : d_data( s ), d_size( count ) {}
inline string_view( const std::string& s ) : d_data( s.data() ), d_size( s.size() ) {}
// Assignment
constexpr string_view& operator=( string_view&& other ) noexcept = default;
constexpr string_view& operator=( const string_view& other ) noexcept = default;
// Iterators
constexpr const char* begin() const noexcept { return d_data; }
constexpr const char* end() const noexcept { return d_data + d_size; }
constexpr const char* cbegin() const noexcept { return begin(); }
constexpr const char* cend() const noexcept { return end(); }
// capacity
constexpr size_t size() const noexcept { return d_size; }
constexpr size_t length() const noexcept { return d_size; }
constexpr bool empty() const noexcept { return d_size == 0; }
// Element access
constexpr const char& operator[]( size_t pos ) const
{
if ( pos >= d_size )
throw std::out_of_range( "string_view[]" );
return d_data[pos];
}
constexpr const char& at( size_t pos ) const
{
if ( pos >= d_size )
throw std::out_of_range( "string_view::at()" );
return d_data[pos];
}
constexpr const char& front() const
{
if ( d_size == 0 )
throw std::out_of_range( "front()" );
return d_data[0];
}
constexpr const char& back() const
{
if ( d_size == 0 )
throw std::out_of_range( "back()" );
return d_data[size() - 1];
}
constexpr const char* data() const noexcept { return d_data; }
// Swap data
void swap( string_view& other ) noexcept
{
std::swap( d_data, other.d_data );
std::swap( d_size, other.d_size );
}
// String operations
size_t copy( char* dest, size_t n, size_t pos = 0 ) const
{
if ( pos > size() )
throw std::out_of_range( "string_view::copy()" );
const size_t rlen = std::min( n, size() - pos );
memcpy( dest, data() + pos, rlen );
return rlen;
}
constexpr string_view substr( size_t pos = 0, size_t n = npos ) const
{
if ( pos > size() )
throw std::out_of_range( "string_view::substr()" );
return string_view( data() + pos, std::min( n, size() - pos ) );
}
// Find
constexpr size_t find( char ch, size_t pos = 0 ) const noexcept
{
for ( size_t i = pos; i < d_size; i++ )
if ( d_data[i] == ch )
return i;
return std::string::npos;
}
constexpr size_t find( string_view v, size_t pos = 0 ) const noexcept
{
size_t i = pos;
size_t N = v.size();
if ( N == 0 || N > ( d_size - pos ) )
return std::string::npos;
while ( i < ( d_size - N + 1 ) ) {
size_t j = 0;
for ( j = 0; j < N && i + j < d_size; j++ )
if ( d_data[i + j] != v[j] )
break;
if ( j == N )
return i;
i++;
}
return std::string::npos;
}
// compare()
constexpr int compare( const string_view& other ) const noexcept
{
int N = std::min( size(), other.size() );
int result = 0;
for ( int i = 0; i < N && result == 0; i++ )
if ( d_data[i] != other[i] )
result = d_data[i] < other[i] ? -i : i;
if ( result == 0 )
result = size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
return result;
}
constexpr int compare( size_t pos1, size_t n1, string_view other ) const
{
return substr( pos1, n1 ).compare( other );
}
constexpr int compare( size_t pos1, size_t n1, string_view other, size_t pos2, size_t n2 ) const
{
return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) );
}
constexpr int compare( char const* s ) const { return compare( string_view( s ) ); }
constexpr int compare( size_t pos1, size_t n1, char const* s ) const
{
return substr( pos1, n1 ).compare( string_view( s ) );
}
constexpr int compare( size_t pos1, size_t n1, char const* s, size_t n2 ) const
{
return substr( pos1, n1 ).compare( string_view( s, n2 ) );
}
explicit operator std::string() const { return std::string( begin(), end() ); }
std::string to_string() const { return std::string( begin(), end() ); }
private:
const char* d_data;
size_t d_size;
};
// Non-member functions:
constexpr inline bool operator==( const string_view& lhs, const string_view& rhs ) noexcept
{
return lhs.compare( rhs ) == 0;
}
constexpr inline bool operator!=( const string_view& lhs, const string_view& rhs ) noexcept
{
return lhs.compare( rhs ) != 0;
}
constexpr inline bool operator<( const string_view& lhs, const string_view& rhs ) noexcept
{
return lhs.compare( rhs ) < 0;
}
constexpr inline bool operator<=( const string_view& lhs, const string_view& rhs ) noexcept
{
return lhs.compare( rhs ) <= 0;
}
constexpr inline bool operator>( const string_view& lhs, const string_view& rhs ) noexcept
{
return lhs.compare( rhs ) > 0;
}
constexpr inline bool operator>=( const string_view& lhs, const string_view& rhs ) noexcept
{
return lhs.compare( rhs ) >= 0;
}
inline std::string to_string( const string_view& v ) { return std::string( v.begin(), v.end() ); }
inline string_view to_string_view( std::string const& s )
{
return string_view( s.data(), s.size() );
}
inline std::ostream& operator<<( std::ostream& out, const string_view& s )
{
out << s.data();
return out;
}
} // namespace StackTrace
#endif