Merge pull request #302 from samisalreadytaken/vscript-debugger

vscript debugger
This commit is contained in:
Blixibon 2025-02-25 00:09:21 -06:00 committed by GitHub
commit e8a76a6ecc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 23870 additions and 12 deletions

View File

@ -28,6 +28,10 @@
extern IScriptManager *scriptmanager;
extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * );
#ifdef MAPBASE_VSCRIPT
extern int vscript_debugger_port;
#endif
// #define VMPROFILE 1
#ifdef VMPROFILE
@ -682,6 +686,14 @@ bool VScriptClientInit()
//g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" );
#endif
#ifdef MAPBASE_VSCRIPT
if ( vscript_debugger_port )
{
g_pScriptVM->ConnectDebugger( vscript_debugger_port );
vscript_debugger_port = 0;
}
#endif
if (scriptLanguage == SL_SQUIRREL)
{
g_pScriptVM->Run( g_Script_vscript_client );
@ -770,11 +782,19 @@ public:
VScriptClientTerm();
}
virtual void FrameUpdatePostEntityThink()
#ifdef MAPBASE_VSCRIPT
virtual void Update( float frametime )
{
if ( g_pScriptVM )
g_pScriptVM->Frame( frametime );
}
#else
virtual void FrameUpdatePostEntityThink()
{
if ( g_pScriptVM )
g_pScriptVM->Frame( gpGlobals->frametime );
}
#endif
bool m_bAllowEntityCreationInScripts;
};

View File

@ -22,6 +22,10 @@
extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * );
#ifdef MAPBASE_VSCRIPT
extern int vscript_debugger_port;
#endif
// #define VMPROFILE 1
#ifdef VMPROFILE
@ -663,6 +667,14 @@ bool VScriptServerInit()
RegisterSharedScriptFunctions();
#endif
#ifdef MAPBASE_VSCRIPT
if ( vscript_debugger_port )
{
g_pScriptVM->ConnectDebugger( vscript_debugger_port );
vscript_debugger_port = 0;
}
#endif
if (scriptLanguage == SL_SQUIRREL)
{
g_pScriptVM->Run( g_Script_vscript_server );

View File

@ -37,6 +37,7 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * );
#ifdef MAPBASE_VSCRIPT
// This is to ensure a dependency exists between the vscript library and the game DLLs
extern int vscript_token;
extern int vscript_debugger_port;
int vscript_token_hack = vscript_token;
#endif
@ -390,12 +391,30 @@ CON_COMMAND_F( script_debug, "Connect the vscript VM to the script debugger", FC
if ( !IsCommandIssuedByServerAdmin() )
return;
#ifdef MAPBASE_VSCRIPT
#ifdef GAME_DLL
int port = 1212;
#else
int port = 1213;
#endif
#endif
if ( !g_pScriptVM )
{
#ifdef MAPBASE_VSCRIPT
vscript_debugger_port = port;
CGMsg( 0, CON_GROUP_VSCRIPT, "VScript VM is not running, waiting for it to attach the debugger to port %d...\n", port );
#else
CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" );
#endif
return;
}
#ifdef MAPBASE_VSCRIPT
g_pScriptVM->ConnectDebugger( port );
#else
g_pScriptVM->ConnectDebugger();
#endif
}
#ifdef CLIENT_DLL

View File

@ -850,7 +850,11 @@ public:
virtual bool Init() = 0;
virtual void Shutdown() = 0;
#ifdef MAPBASE_VSCRIPT
virtual bool ConnectDebugger( int port = 0 ) = 0;
#else
virtual bool ConnectDebugger() = 0;
#endif
virtual void DisconnectDebugger() = 0;
virtual ScriptLanguage_t GetLanguage() = 0;

View File

@ -0,0 +1,70 @@
//-----------------------------------------------------------------------
// github.com/samisalreadytaken/sqdbg
//-----------------------------------------------------------------------
//
// Squirrel Debugger
//
#ifndef SQDBG_H
#define SQDBG_H
#include <squirrel.h>
#define SQDBG_SV_API_VER 1
#ifndef SQDBG_API
#ifdef SQDBG_DLL
#if defined(_WIN32)
#ifdef SQDBG_DLL_EXPORT
#define SQDBG_API __declspec(dllexport)
#else
#define SQDBG_API __declspec(dllimport)
#endif
#elif defined(__GNUC__)
#ifdef SQDBG_DLL_EXPORT
#define SQDBG_API __attribute__((visibility("default")))
#else
#define SQDBG_API extern
#endif
#else
#define SQDBG_API extern
#endif
#else
#define SQDBG_API extern
#endif
#endif
struct SQDebugServer;
typedef SQDebugServer* HSQDEBUGSERVER;
#ifdef __cplusplus
extern "C" {
#endif
// Create and attach a new debugger
// Memory is owned by the VM, it is freed when the VM dies or
// the debugger is disconnected via sqdbg_destroy_debugger()
SQDBG_API HSQDEBUGSERVER sqdbg_attach_debugger( HSQUIRRELVM vm );
// Detach and destroy the debugger attached to this VM
// Invalidates the handle returned from sqdbg_attach_debugger()
SQDBG_API void sqdbg_destroy_debugger( HSQUIRRELVM vm );
// Open specified port and allow client connections
// If port is 0, the system will choose a unique available port
// Returns 0 on success
SQDBG_API int sqdbg_listen_socket( HSQDEBUGSERVER dbg, unsigned short port );
// Process client connections and incoming messages
// Blocks on script breakpoints while a client is connected
SQDBG_API void sqdbg_frame( HSQDEBUGSERVER dbg );
// Copies the script to be able to source it to debugger clients
SQDBG_API void sqdbg_on_script_compile( HSQDEBUGSERVER dbg, const SQChar *script, SQInteger size,
const SQChar *sourcename, SQInteger sourcenamelen );
#ifdef __cplusplus
}
#endif
#endif // SQDBG_H

View File

@ -0,0 +1,131 @@
//-----------------------------------------------------------------------
// github.com/samisalreadytaken/sqdbg
//-----------------------------------------------------------------------
//
#ifndef SQDBG_DEBUG_H
#define SQDBG_DEBUG_H
#if 0
#ifdef _DEBUG
#ifdef _WIN32
#include <crtdbg.h>
bool __IsDebuggerPresent();
const char *GetModuleBaseName();
#define DebuggerBreak() do { if ( __IsDebuggerPresent() ) __debugbreak(); } while(0)
#define Assert( x ) \
do { \
__CAT( L, __LINE__ ): \
if ( !(x) && (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, GetModuleBaseName(), #x)) ) \
{ \
if ( !__IsDebuggerPresent() ) \
goto __CAT( L, __LINE__ ); \
__debugbreak(); \
} \
} while(0)
#define AssertMsg( x, msg ) \
do { \
__CAT( L, __LINE__ ): \
if ( !(x) && (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, GetModuleBaseName(), msg)) ) \
{ \
if ( !__IsDebuggerPresent() ) \
goto __CAT( L, __LINE__ ); \
__debugbreak(); \
} \
} while(0)
#define AssertMsg1( x, msg, a1 ) \
do { \
__CAT( L, __LINE__ ): \
if ( !(x) && (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, GetModuleBaseName(), msg, a1)) ) \
{ \
if ( !__IsDebuggerPresent() ) \
goto __CAT( L, __LINE__ ); \
__debugbreak(); \
} \
} while(0)
#define AssertMsg2( x, msg, a1, a2 ) \
do { \
__CAT( L, __LINE__ ): \
if ( !(x) && (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, GetModuleBaseName(), msg, a1, a2)) ) \
{ \
if ( !__IsDebuggerPresent() ) \
goto __CAT( L, __LINE__ ); \
__debugbreak(); \
} \
} while(0)
#else
extern "C" int printf(const char *, ...);
#define DebuggerBreak() asm("int3")
#define Assert( x ) \
do { \
if ( !(x) ) \
{ \
::printf("Assertion failed %s:%d: %s\n", __FILE__, __LINE__, #x); \
DebuggerBreak(); \
} \
} while(0)
#define AssertMsg( x, msg ) \
do { \
if ( !(x) ) \
{ \
::printf("Assertion failed %s:%d: %s\n", __FILE__, __LINE__, msg); \
DebuggerBreak(); \
} \
} while(0)
#define AssertMsg1( x, msg, a1 ) \
do { \
if ( !(x) ) \
{ \
::printf("Assertion failed %s:%d: ", __FILE__, __LINE__); \
::printf(msg, a1); \
::printf("\n"); \
DebuggerBreak(); \
} \
} while(0)
#define AssertMsg2( x, msg, a1, a2 ) \
do { \
if ( !(x) ) \
{ \
::printf("Assertion failed %s:%d: ", __FILE__, __LINE__); \
::printf(msg, a1, a2); \
::printf("\n"); \
DebuggerBreak(); \
} \
} while(0)
#endif
#define Verify( x ) Assert(x)
#else
#define DebuggerBreak() ((void)0)
#define Assert( x ) ((void)0)
#define AssertMsg( x, msg ) ((void)0)
#define AssertMsg1( x, msg, a1 ) ((void)0)
#define AssertMsg2( x, msg, a1, a2 ) ((void)0)
#define Verify( x ) x
#endif // _DEBUG
#endif
#include <tier0/dbg.h>
// Misdefined for GCC in platform.h
#undef UNREACHABLE
#ifdef _WIN32
#define UNREACHABLE() do { Assert(!"UNREACHABLE"); __assume(0); } while(0)
#else
#define UNREACHABLE() do { Assert(!"UNREACHABLE"); __builtin_unreachable(); } while(0)
#endif
#endif // SQDBG_DEBUG_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,846 @@
//-----------------------------------------------------------------------
// github.com/samisalreadytaken/sqdbg
//-----------------------------------------------------------------------
//
#ifndef SQDBG_NET_H
#define SQDBG_NET_H
#ifdef _WIN32
#include <WinSock2.h>
#include <WS2tcpip.h>
#ifdef _DEBUG
#include <debugapi.h>
inline bool __IsDebuggerPresent()
{
return IsDebuggerPresent();
}
inline const char *GetModuleBaseName()
{
static char module[MAX_PATH];
int len = GetModuleFileNameA( NULL, module, sizeof(module) );
if ( len != 0 )
{
for ( char *pBase = module + len; pBase-- > module; )
{
if ( *pBase == '\\' )
return pBase + 1;
}
return module;
}
return "";
}
#endif
#pragma comment(lib, "Ws2_32.lib")
#undef RegisterClass
#undef SendMessage
#undef Yield
#undef CONST
#undef PURE
#undef errno
#define errno WSAGetLastError()
#define strerr(e) gai_strerror(e)
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define closesocket close
#define ioctlsocket ioctl
#define strerr(e) strerror(e)
typedef int SOCKET;
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define SD_BOTH SHUT_RDWR
#endif
#ifdef _DEBUG
class CEntryCounter
{
public:
int *count;
CEntryCounter( int *p ) : count(p) { (*count)++; }
~CEntryCounter() { (*count)--; }
};
#define TRACK_ENTRIES() \
static int s_EntryCount = 0; \
CEntryCounter entrycounter( &s_EntryCount );
#else
#define TRACK_ENTRIES()
#endif
void *sqdbg_malloc( unsigned int size );
void *sqdbg_realloc( void *p, unsigned int oldsize, unsigned int size );
void sqdbg_free( void *p, unsigned int size );
#ifndef SQDBG_NET_BUF_SIZE
#define SQDBG_NET_BUF_SIZE ( 16 * 1024 )
#endif
class CMessagePool
{
public:
typedef int index_t;
#pragma pack(push, 4)
struct message_t
{
index_t next;
index_t prev;
unsigned short len;
char ptr[1];
};
#pragma pack(pop)
struct chunk_t
{
char *ptr;
int count;
};
static const index_t INVALID_INDEX = 0x80000000;
// Message queue is going to be less than 16 unless
// there is many variable evaluations at once or network lag
static const int MEM_CACHE_CHUNKS_ALIGN = 16;
// Most messages are going to be less than 256 bytes,
// only exceeding it on long file paths and long evaluate strings
static const int MEM_CACHE_CHUNKSIZE = 256;
message_t *Get( index_t index )
{
Assert( index != INVALID_INDEX );
int msgIdx = index & 0x0000ffff;
int chunkIdx = index >> 16;
Assert( m_Memory );
Assert( chunkIdx < m_MemChunkCount );
chunk_t *chunk = &m_Memory[ chunkIdx ];
Assert( msgIdx < chunk->count );
return (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
}
chunk_t *m_Memory;
int m_MemChunkCount;
int m_ElemCount;
index_t m_Head;
index_t m_Tail;
index_t NewMessage( char *pcsMsg, int nLength )
{
if ( !m_Memory )
{
m_Memory = (chunk_t*)sqdbg_malloc( m_MemChunkCount * sizeof(chunk_t) );
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
memset( (char*)m_Memory, 0, m_MemChunkCount * sizeof(chunk_t) );
chunk_t *chunk = &m_Memory[0];
chunk->count = MEM_CACHE_CHUNKS_ALIGN;
chunk->ptr = (char*)sqdbg_malloc( chunk->count * MEM_CACHE_CHUNKSIZE );
AssertOOM( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
memset( chunk->ptr, 0, chunk->count * MEM_CACHE_CHUNKSIZE );
}
int requiredChunks = ( sizeof(message_t) + nLength - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
int matchedChunks = 0;
int msgIdx = 0;
int chunkIdx = 0;
for (;;)
{
chunk_t *chunk = &m_Memory[ chunkIdx ];
Assert( chunk->count && chunk->ptr );
message_t *msg = (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
if ( msg->len == 0 )
{
if ( ++matchedChunks == requiredChunks )
{
msgIdx = msgIdx - matchedChunks + 1;
msg = (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
Assert( nLength >= 0 );
Assert( nLength < ( 1 << ( sizeof(message_t::len) * 8 ) ) );
msg->next = msg->prev = INVALID_INDEX;
msg->len = (unsigned short)nLength;
memcpy( msg->ptr, pcsMsg, nLength );
return ( chunkIdx << 16 ) | msgIdx;
}
}
else
{
matchedChunks = 0;
}
msgIdx += ( sizeof(message_t) + msg->len - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
Assert( msgIdx < 0x0000ffff );
if ( msgIdx < chunk->count )
continue;
msgIdx = 0;
matchedChunks = 0;
if ( ++chunkIdx >= m_MemChunkCount )
{
int oldcount = m_MemChunkCount;
m_MemChunkCount += 4;
m_Memory = (chunk_t*)sqdbg_realloc( m_Memory,
oldcount * sizeof(chunk_t),
m_MemChunkCount * sizeof(chunk_t) );
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
memset( (char*)m_Memory + oldcount * sizeof(chunk_t),
0,
(m_MemChunkCount - oldcount) * sizeof(chunk_t) );
}
chunk = &m_Memory[ chunkIdx ];
if ( chunk->count == 0 )
{
Assert( chunk->ptr == NULL );
chunk->count = ( requiredChunks + ( MEM_CACHE_CHUNKS_ALIGN - 1 ) ) & ~( MEM_CACHE_CHUNKS_ALIGN - 1 );
chunk->ptr = (char*)sqdbg_malloc( chunk->count * MEM_CACHE_CHUNKSIZE );
AssertOOM( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
memset( chunk->ptr, 0, chunk->count * MEM_CACHE_CHUNKSIZE );
}
Assert( chunkIdx < 0x00007fff );
}
}
void DeleteMessage( message_t *pMsg )
{
if ( pMsg->len == 0 )
return;
Assert( pMsg->len > 0 );
Assert( m_ElemCount > 0 );
m_ElemCount--;
int msgIdx = ( ( sizeof(message_t) + pMsg->len +
( MEM_CACHE_CHUNKSIZE - 1 ) ) & ~( MEM_CACHE_CHUNKSIZE - 1 ) ) / MEM_CACHE_CHUNKSIZE;
do
{
pMsg->next = pMsg->prev = INVALID_INDEX;
pMsg->len = 0;
pMsg->ptr[0] = 0;
pMsg = (message_t*)( (char*)pMsg + MEM_CACHE_CHUNKSIZE );
}
while ( --msgIdx > 0 );
}
public:
CMessagePool() :
m_Memory( NULL ),
m_MemChunkCount( 4 ),
m_ElemCount( 0 ),
m_Head( INVALID_INDEX ),
m_Tail( INVALID_INDEX )
{
}
~CMessagePool()
{
if ( m_Memory )
{
for ( int chunkIdx = 0; chunkIdx < m_MemChunkCount; chunkIdx++ )
{
chunk_t *chunk = &m_Memory[ chunkIdx ];
for ( int msgIdx = 0; msgIdx < chunk->count; )
{
message_t *msg = (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
Assert( msg->len == 0 && msg->ptr[0] == 0 );
msgIdx += ( sizeof(message_t) + msg->len - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
DeleteMessage( msg );
}
sqdbg_free( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
}
sqdbg_free( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
}
Assert( m_ElemCount == 0 );
}
void Shrink()
{
Assert( m_ElemCount == 0 );
if ( !m_Memory )
return;
for ( int chunkIdx = 1; chunkIdx < m_MemChunkCount; chunkIdx++ )
{
chunk_t *chunk = &m_Memory[ chunkIdx ];
if ( chunk->count )
{
#ifdef _DEBUG
for ( int msgIdx = 0; msgIdx < chunk->count; )
{
message_t *msg = (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
Assert( msg->len == 0 && msg->ptr[0] == 0 );
msgIdx += ( sizeof(message_t) + msg->len - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
}
#endif
sqdbg_free( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
chunk->count = 0;
chunk->ptr = NULL;
}
}
if ( m_MemChunkCount > 4 )
{
int oldcount = m_MemChunkCount;
m_MemChunkCount = 4;
m_Memory = (chunk_t*)sqdbg_realloc( m_Memory,
oldcount * sizeof(chunk_t),
m_MemChunkCount * sizeof(chunk_t) );
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
}
}
void Add( char *pcsMsg, int nLength )
{
index_t newMsg = NewMessage( pcsMsg, nLength );
m_ElemCount++;
// Add to tail
if ( m_Tail == INVALID_INDEX )
{
Assert( m_Head == INVALID_INDEX );
m_Head = m_Tail = newMsg;
}
else
{
Get(newMsg)->prev = m_Tail;
Get(m_Tail)->next = newMsg;
m_Tail = newMsg;
}
}
template < typename T, void (T::*callback)( char *ptr, int len ) >
void Service( T *ctx )
{
TRACK_ENTRIES();
index_t msg = m_Head;
while ( msg != INVALID_INDEX )
{
message_t *pMsg = Get(msg);
Assert( pMsg->len || ( pMsg->next == INVALID_INDEX && pMsg->prev == INVALID_INDEX ) );
if ( pMsg->len == 0 )
break;
// Advance before execution
index_t next = pMsg->next;
index_t prev = pMsg->prev;
pMsg->next = INVALID_INDEX;
pMsg->prev = INVALID_INDEX;
if ( prev != INVALID_INDEX )
Get(prev)->next = next;
if ( next != INVALID_INDEX )
Get(next)->prev = prev;
if ( msg == m_Head )
{
// prev could be non-null on re-entry
//Assert( prev == INVALID_INDEX );
m_Head = next;
}
if ( msg == m_Tail )
{
Assert( next == INVALID_INDEX && prev == INVALID_INDEX );
m_Tail = INVALID_INDEX;
}
(ctx->*callback)( pMsg->ptr, pMsg->len );
Assert( Get(msg) == pMsg );
DeleteMessage( pMsg );
msg = next;
}
}
void Clear()
{
index_t msg = m_Head;
while ( msg != INVALID_INDEX )
{
message_t *pMsg = Get(msg);
index_t next = pMsg->next;
index_t prev = pMsg->prev;
if ( prev != INVALID_INDEX )
Get(prev)->next = next;
if ( next != INVALID_INDEX )
Get(next)->prev = prev;
if ( msg == m_Head )
{
Assert( prev == INVALID_INDEX );
m_Head = next;
}
if ( msg == m_Tail )
{
Assert( next == INVALID_INDEX && prev == INVALID_INDEX );
m_Tail = INVALID_INDEX;
}
DeleteMessage( pMsg );
msg = next;
}
Assert( m_Head == INVALID_INDEX && m_Tail == INVALID_INDEX );
}
};
static inline bool SocketWouldBlock()
{
#ifdef _WIN32
return WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS;
#else
return errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS;
#endif
}
static inline void CloseSocket( SOCKET *sock )
{
if ( *sock != INVALID_SOCKET )
{
shutdown( *sock, SD_BOTH );
closesocket( *sock );
*sock = INVALID_SOCKET;
}
}
class CServerSocket
{
private:
SOCKET m_Socket;
SOCKET m_ServerSocket;
CMessagePool m_MessagePool;
char *m_pRecvBufPtr;
char m_pRecvBuf[ SQDBG_NET_BUF_SIZE ];
bool m_bWSAInit;
public:
const char *m_pszLastMsgFmt;
const char *m_pszLastMsg;
public:
bool IsListening()
{
return m_ServerSocket != INVALID_SOCKET;
}
bool IsClientConnected()
{
return m_Socket != INVALID_SOCKET;
}
unsigned short GetServerPort()
{
if ( m_ServerSocket != INVALID_SOCKET )
{
sockaddr_in addr;
socklen_t len = sizeof(addr);
if ( getsockname( m_ServerSocket, (sockaddr*)&addr, &len ) != SOCKET_ERROR )
return ntohs( addr.sin_port );
}
return 0;
}
bool ListenSocket( unsigned short port )
{
if ( m_ServerSocket != INVALID_SOCKET )
return true;
#ifdef _WIN32
if ( !m_bWSAInit )
{
WSADATA wsadata;
if ( WSAStartup( MAKEWORD(2,2), &wsadata ) != 0 )
{
int err = errno;
m_pszLastMsgFmt = "(sqdbg) WSA startup failed";
m_pszLastMsg = strerr(err);
return false;
}
m_bWSAInit = true;
}
#endif
m_ServerSocket = socket( AF_INET, SOCK_STREAM, 0 );
if ( m_ServerSocket == INVALID_SOCKET )
{
int err = errno;
Shutdown();
m_pszLastMsgFmt = "(sqdbg) Failed to open socket";
m_pszLastMsg = strerr(err);
return false;
}
u_long iMode = 1;
#ifdef _WIN32
if ( ioctlsocket( m_ServerSocket, FIONBIO, &iMode ) == SOCKET_ERROR )
#else
int f = fcntl( m_ServerSocket, F_GETFL );
if ( f == -1 || fcntl( m_ServerSocket, F_SETFL, f | O_NONBLOCK ) == -1 )
#endif
{
int err = errno;
Shutdown();
m_pszLastMsgFmt = "(sqdbg) Failed to set socket non-blocking";
m_pszLastMsg = strerr(err);
return false;
}
iMode = 1;
if ( setsockopt( m_ServerSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&iMode, sizeof(iMode) ) == SOCKET_ERROR )
{
int err = errno;
Shutdown();
m_pszLastMsgFmt = "(sqdbg) Failed to set TCP nodelay";
m_pszLastMsg = strerr(err);
return false;
}
linger ln;
ln.l_onoff = 0;
ln.l_linger = 0;
if ( setsockopt( m_ServerSocket, SOL_SOCKET, SO_LINGER, (char*)&ln, sizeof(ln) ) == SOCKET_ERROR )
{
int err = errno;
Shutdown();
m_pszLastMsgFmt = "(sqdbg) Failed to set don't linger";
m_pszLastMsg = strerr(err);
return false;
}
sockaddr_in addr;
memset( &addr, 0, sizeof(addr) );
addr.sin_family = AF_INET;
addr.sin_port = htons( port );
addr.sin_addr.s_addr = htonl( INADDR_ANY );
if ( bind( m_ServerSocket, (sockaddr*)&addr, sizeof(addr) ) == SOCKET_ERROR )
{
int err = errno;
Shutdown();
m_pszLastMsgFmt = "(sqdbg) Failed to bind socket on port";
m_pszLastMsg = strerr(err);
return false;
}
if ( listen( m_ServerSocket, 0 ) == SOCKET_ERROR )
{
int err = errno;
Shutdown();
m_pszLastMsgFmt = "(sqdbg) Failed to listen to socket";
m_pszLastMsg = strerr(err);
return false;
}
return true;
}
bool Listen()
{
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
fd_set rfds;
FD_ZERO( &rfds );
FD_SET( m_ServerSocket, &rfds );
select( 0, &rfds, NULL, NULL, &tv );
if ( !FD_ISSET( m_ServerSocket, &rfds ) )
return false;
FD_CLR( m_ServerSocket, &rfds );
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
m_Socket = accept( m_ServerSocket, (sockaddr*)&addr, &addrlen );
if ( m_Socket == INVALID_SOCKET )
return false;
#ifndef _WIN32
int f = fcntl( m_Socket, F_GETFL );
if ( f == -1 || fcntl( m_Socket, F_SETFL, f | O_NONBLOCK ) == -1 )
{
int err = errno;
DisconnectClient();
m_pszLastMsgFmt = "(sqdbg) Failed to set socket non-blocking";
m_pszLastMsg = strerr(err);
return false;
}
#endif
m_pszLastMsg = inet_ntoa( addr.sin_addr );
return true;
}
void Shutdown()
{
CloseSocket( &m_Socket );
CloseSocket( &m_ServerSocket );
#ifdef _WIN32
if ( m_bWSAInit )
{
WSACleanup();
m_bWSAInit = false;
}
#endif
m_MessagePool.Clear();
m_pRecvBufPtr = m_pRecvBuf;
memset( m_pRecvBuf, -1, sizeof( m_pRecvBuf ) );
}
void DisconnectClient()
{
CloseSocket( &m_Socket );
m_MessagePool.Clear();
m_pRecvBufPtr = m_pRecvBuf;
memset( m_pRecvBuf, -1, sizeof( m_pRecvBuf ) );
}
bool Send( const char *buf, int len )
{
for (;;)
{
int bytesSend = send( m_Socket, buf, len, 0 );
if ( bytesSend == SOCKET_ERROR )
{
// Keep blocking
if ( SocketWouldBlock() )
continue;
int err = errno;
DisconnectClient();
m_pszLastMsgFmt = "(sqdbg) Network error";
m_pszLastMsg = strerr(err);
return false;
}
if ( len == bytesSend )
return true;
len -= bytesSend;
}
}
bool Recv()
{
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
fd_set rfds;
FD_ZERO( &rfds );
FD_SET( m_Socket, &rfds );
select( 0, &rfds, NULL, NULL, &tv );
if ( !FD_ISSET( m_Socket, &rfds ) )
return true;
FD_CLR( m_Socket, &rfds );
u_long readlen = 0;
ioctlsocket( m_Socket, FIONREAD, &readlen );
int bufsize = m_pRecvBuf + sizeof(m_pRecvBuf) - m_pRecvBufPtr;
if ( bufsize <= 0 || (unsigned int)bufsize < readlen )
{
DisconnectClient();
m_pszLastMsgFmt = "(sqdbg) Net message buffer is full";
m_pszLastMsg = NULL;
return false;
}
for (;;)
{
int bytesRecv = recv( m_Socket, m_pRecvBufPtr, bufsize, 0 );
if ( bytesRecv == SOCKET_ERROR )
{
if ( SocketWouldBlock() )
break;
int err = errno;
DisconnectClient();
m_pszLastMsgFmt = "(sqdbg) Network error";
m_pszLastMsg = strerr(err);
return false;
}
if ( !bytesRecv )
{
#ifdef _WIN32
WSASetLastError( WSAECONNRESET );
#else
errno = ECONNRESET;
#endif
int err = errno;
DisconnectClient();
m_pszLastMsgFmt = "(sqdbg) Client disconnected";
m_pszLastMsg = strerr(err);
return false;
}
m_pRecvBufPtr += bytesRecv;
bufsize -= bytesRecv;
}
return true;
}
//
// Header reader sets message pointer to the content start
//
template < bool (readHeader)( char **ppMsg, int *pLength ) >
bool Parse()
{
// Nothing to parse
if ( m_pRecvBufPtr == m_pRecvBuf )
return true;
char *pMsg = m_pRecvBuf;
int nLength = sizeof(m_pRecvBuf);
while ( readHeader( &pMsg, &nLength ) )
{
char *pMsgEnd = pMsg + (unsigned int)nLength;
if ( pMsgEnd >= m_pRecvBuf + sizeof(m_pRecvBuf) )
{
DisconnectClient();
m_pszLastMsgFmt = "(sqdbg) Client disconnected";
if ( nLength == -1 )
{
m_pszLastMsg = "malformed message";
}
else
{
m_pszLastMsg = "content is too large";
}
return false;
}
// Entire message wasn't received, wait for it
if ( m_pRecvBufPtr < pMsgEnd )
break;
m_MessagePool.Add( pMsg, nLength );
// Last message
if ( m_pRecvBufPtr == pMsgEnd )
{
memset( m_pRecvBuf, 0, m_pRecvBufPtr - m_pRecvBuf );
m_pRecvBufPtr = m_pRecvBuf;
break;
}
// Next message
int shift = m_pRecvBufPtr - pMsgEnd;
memmove( m_pRecvBuf, pMsgEnd, shift );
memset( m_pRecvBuf + shift, 0, m_pRecvBufPtr - ( m_pRecvBuf + shift ) );
m_pRecvBufPtr = m_pRecvBuf + shift;
pMsg = m_pRecvBuf;
nLength = sizeof(m_pRecvBuf);
}
return true;
}
template < typename T, void (T::*callback)( char *ptr, int len ) >
void Execute( T *ctx )
{
m_MessagePool.Service< T, callback >( ctx );
if ( m_Socket == INVALID_SOCKET && m_MessagePool.m_ElemCount == 0 )
{
m_MessagePool.Shrink();
}
}
public:
CServerSocket() :
m_Socket( INVALID_SOCKET ),
m_ServerSocket( INVALID_SOCKET ),
m_pRecvBufPtr( m_pRecvBuf ),
m_bWSAInit( false )
{
Assert( sizeof(m_pRecvBuf) <= ( 1 << ( sizeof(CMessagePool::message_t::len) * 8 ) ) );
}
};
#endif // SQDBG_NET_H

View File

@ -0,0 +1,300 @@
//-----------------------------------------------------------------------
// github.com/samisalreadytaken/sqdbg
//-----------------------------------------------------------------------
//
#ifndef SQDBG_DAP_H
#define SQDBG_DAP_H
#define DAP_HEADER_CONTENTLENGTH "Content-Length"
#define DAP_HEADER_END "\r\n\r\n"
#define DAP_HEADER_MAXSIZE ( STRLEN(DAP_HEADER_CONTENTLENGTH ": ") + STRLEN(DAP_HEADER_END) + FMT_UINT32_LEN )
inline void DAP_Serialise( CBuffer *buffer )
{
Assert( buffer->size() > 0 && buffer->size() < INT_MAX );
char *mem = buffer->base();
int contentSize = buffer->size() - DAP_HEADER_MAXSIZE;
int digits = countdigits( contentSize );
int padding = FMT_UINT32_LEN - digits;
int nearest = 10;
while ( contentSize >= nearest )
nearest *= 10;
contentSize += padding;
if ( contentSize >= nearest )
{
// Padding between header and content increased content size digits,
// add padding in the end to match
padding--;
digits++;
buffer->_base.Ensure( buffer->size() + 1 );
mem[buffer->_size++] = ' ';
}
memcpy( mem, DAP_HEADER_CONTENTLENGTH ": ", STRLEN(DAP_HEADER_CONTENTLENGTH ": ") );
int idx = STRLEN(DAP_HEADER_CONTENTLENGTH ": ") + digits;
for ( int i = idx - 1; contentSize; )
{
char c = contentSize % 10;
contentSize /= 10;
mem[i--] = '0' + c;
}
memcpy( mem + idx, DAP_HEADER_END, STRLEN(DAP_HEADER_END) );
idx += STRLEN(DAP_HEADER_END);
memset( mem + idx, ' ', padding );
}
inline void DAP_Free( CBuffer *buffer )
{
buffer->_size = 0;
}
static inline void ParseFieldName( const char *pMemEnd, char *pStart, int *len )
{
char *c = pStart;
for (;;)
{
if ( IN_RANGE_CHAR( ((unsigned char*)c)[0], 0x20, 0x7E ) )
{
if ( c + 1 >= pMemEnd )
{
*len = -1;
return;
}
if ( c[0] == ':' )
{
if ( c[1] == ' ' )
{
*len = c - pStart;
return;
}
*len = 0;
return;
}
c++;
}
else
{
*len = 0;
return;
}
}
}
static inline void ParseFieldValue( const char *pMemEnd, char *pStart, int *len )
{
char *c = pStart;
for (;;)
{
if ( c + 1 >= pMemEnd )
{
*len = -1;
return;
}
if ( c[0] == '\n' )
{
*len = 0;
return;
}
if ( c[0] == '\r' && c[1] == '\n' )
{
*len = c - pStart;
return;
}
c++;
}
}
inline bool DAP_ReadHeader( char **ppMsg, int *pLength )
{
char *pMsg = *ppMsg;
const char *pMemEnd = pMsg + *pLength;
int nContentLength = 0;
for (;;)
{
int len;
ParseFieldName( pMemEnd, pMsg, &len );
if ( len == 0 )
goto invalid;
if ( len == -1 )
return false;
if ( len == (int)STRLEN(DAP_HEADER_CONTENTLENGTH) &&
!memcmp( pMsg, DAP_HEADER_CONTENTLENGTH, STRLEN(DAP_HEADER_CONTENTLENGTH) ) )
{
// Duplicate length field
if ( nContentLength )
goto ignore;
pMsg += len + 2;
for ( char *pStart = pMsg;; )
{
if ( pMsg >= pMemEnd )
return false;
if ( IN_RANGE_CHAR( *(unsigned char*)pMsg, '0', '9' ) )
{
nContentLength = nContentLength * 10 + *pMsg - '0';
pMsg++;
if ( pMsg - pStart > (int)FMT_UINT32_LEN )
goto invalid;
}
// Strict - no whitespace allowed
else
{
if ( pMsg + 1 >= pMemEnd )
return false;
if ( pMsg[0] == '\r' && pMsg[1] == '\n' )
{
if ( nContentLength <= 0 )
goto invalid;
*pLength = nContentLength;
pMsg += 2;
break;
}
else
{
goto invalid;
}
}
}
}
// Ignore unknown header fields
else
{
ignore:
pMsg += len + 2;
ParseFieldValue( pMemEnd, pMsg, &len );
if ( len == 0 )
goto invalid;
if ( len == -1 )
return false;
pMsg += len + 2;
}
if ( pMsg + 1 >= pMemEnd )
return false;
if ( pMsg[0] == '\r' && pMsg[1] == '\n' )
{
*ppMsg = pMsg + 2;
return true;
}
}
invalid:
// Signal that the client needs to be dropped
*pLength = -1;
*ppMsg = pMsg;
return true;
}
#ifdef SQDBG_VALIDATE_SENT_MSG
inline void DAP_Test( CScratch< JSON_SCRATCH_CHUNK_SIZE > *scratch, CBuffer *buffer )
{
char *pMsg = buffer->base();
int nLength = buffer->size();
bool res = DAP_ReadHeader( &pMsg, &nLength );
Assert( res && nLength < buffer->size() );
if ( res )
{
json_table_t table;
JSONParser parser( scratch, pMsg, nLength, &table );
AssertMsg1( !parser.GetError(), "%s", parser.GetError() );
}
}
#else
#define DAP_Test(...) (void)0
#endif
#define _DAP_INIT_BUF( _buf ) \
CBufTmpCache _bufcache( (_buf) ); \
(_buf)->_size = DAP_HEADER_MAXSIZE; \
(void)0
#define DAP_START_REQUEST( _seq, _cmd ) \
{ \
_DAP_INIT_BUF( &m_SendBuf ); \
{ \
wjson_table_t packet( m_SendBuf ); \
packet.SetInt( "seq", _seq ); \
packet.SetString( "type", "request" ); \
packet.SetString( "command", _cmd );
#define _DAP_START_RESPONSE( _seq, _cmd, _suc ) \
if ( IsClientConnected() ) \
{ \
_DAP_INIT_BUF( &m_SendBuf ); \
{ \
wjson_table_t packet( m_SendBuf ); \
packet.SetInt( "request_seq", _seq ); \
packet.SetString( "type", "response" ); \
packet.SetString( "command", _cmd ); \
packet.SetBool( "success", _suc );
#define DAP_START_RESPONSE( _seq, _cmd ) \
_DAP_START_RESPONSE( _seq, _cmd, true );
#define DAP_ERROR_RESPONSE( _seq, _cmd ) \
_DAP_START_RESPONSE( _seq, _cmd, false );
#define DAP_ERROR_BODY( _id, _fmt ) \
wjson_table_t body = packet.SetTable( "body" ); \
wjson_table_t error = body.SetTable( "error" ); \
error.SetInt( "id", _id ); \
error.SetString( "format", _fmt ); \
#define DAP_START_EVENT( _seq, _ev ) \
{ \
_DAP_INIT_BUF( &m_SendBuf ); \
{ \
wjson_table_t packet( m_SendBuf ); \
packet.SetInt( "seq", _seq ); \
packet.SetString( "type", "event" ); \
packet.SetString( "event", _ev );
#define DAP_SET( _key, _val ) \
packet.Set( _key, _val );
#define DAP_SET_TABLE( _val ) \
wjson_table_t _val = packet.SetTable( #_val );
#define DAP_SEND() \
} \
\
DAP_Serialise( &m_SendBuf ); \
Send( m_SendBuf.base(), m_SendBuf.size() ); \
DAP_Test( &m_ReadBuf, &m_SendBuf ); \
DAP_Free( &m_SendBuf ); \
}
#endif // SQDBG_DAP_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,624 @@
//-----------------------------------------------------------------------
// github.com/samisalreadytaken/sqdbg
//-----------------------------------------------------------------------
//
#ifndef SQDBG_VEC_H
#define SQDBG_VEC_H
void *sqdbg_malloc( unsigned int size );
void *sqdbg_realloc( void *p, unsigned int oldsize, unsigned int size );
void sqdbg_free( void *p, unsigned int size );
class CMemory
{
public:
#pragma pack(push, 4)
struct memory_t
{
char *ptr;
unsigned int size;
};
#pragma pack(pop)
memory_t memory;
char &operator[]( unsigned int i ) const
{
Assert( memory.size > 0 );
return *( memory.ptr + i );
}
char *Base()
{
return memory.ptr;
}
unsigned int Size() const
{
return memory.size;
}
void Free()
{
if ( memory.ptr )
{
sqdbg_free( memory.ptr, memory.size );
memory.ptr = 0;
memory.size = 0;
}
}
void Alloc( unsigned int count )
{
Assert( count > 0 || memory.size == 0 );
if ( count == memory.size )
return;
const int size = ( count + sizeof(void*) - 1 ) & ~( sizeof(void*) - 1 );
if ( memory.ptr )
{
memory.ptr = (char*)sqdbg_realloc( memory.ptr, memory.size, size );
}
else
{
memory.ptr = (char*)sqdbg_malloc( size );
}
AssertOOM( memory.ptr, size );
if ( memory.ptr )
{
memory.size = size;
}
else
{
memory.size = 0;
}
}
void Ensure( unsigned int newcount )
{
unsigned int oldcount = memory.size;
if ( newcount <= oldcount )
return;
if ( oldcount != 0 )
{
unsigned int i = (unsigned int)0x7fffffffu;
while ( ( i >> 1 ) > newcount )
{
i >>= 1;
}
Assert( i > 0 );
Assert( i > oldcount );
Assert( i >= newcount );
newcount = i;
}
else
{
if ( newcount < 4 )
newcount = 4;
}
Alloc( newcount );
}
};
template< int MEM_CACHE_CHUNKS_ALIGN = 2048 >
class CScratch
{
public:
static const int MEM_CACHE_CHUNKSIZE = 4;
static const int INVALID_INDEX = 0x80000000;
struct chunk_t
{
char *ptr;
int count;
};
chunk_t *m_Memory;
int m_MemChunkCount;
int m_LastFreeChunk;
int m_LastFreeIndex;
char *Get( int index )
{
Assert( index != INVALID_INDEX );
int msgIdx = index & 0x0000ffff;
int chunkIdx = index >> 16;
Assert( m_Memory );
Assert( chunkIdx < m_MemChunkCount );
chunk_t *chunk = &m_Memory[ chunkIdx ];
Assert( msgIdx < chunk->count );
return &chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
}
char *Alloc( int size, int *index = NULL, bool sequential = true )
{
if ( !m_Memory )
{
m_MemChunkCount = 4;
m_Memory = (chunk_t*)sqdbg_malloc( m_MemChunkCount * sizeof(chunk_t) );
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
memset( (char*)m_Memory, 0, m_MemChunkCount * sizeof(chunk_t) );
chunk_t *chunk = &m_Memory[0];
chunk->count = MEM_CACHE_CHUNKS_ALIGN;
chunk->ptr = (char*)sqdbg_malloc( chunk->count * MEM_CACHE_CHUNKSIZE );
AssertOOM( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
memset( chunk->ptr, 0, chunk->count * MEM_CACHE_CHUNKSIZE );
}
int requiredChunks;
int msgIdx;
int chunkIdx;
int matchedChunks = 0;
if ( sequential )
{
requiredChunks = ( size - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
msgIdx = m_LastFreeIndex;
chunkIdx = m_LastFreeChunk;
}
else
{
requiredChunks = ( size + sizeof(int) - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
msgIdx = 0;
chunkIdx = 0;
}
for (;;)
{
chunk_t *chunk = &m_Memory[ chunkIdx ];
Assert( chunk->count && chunk->ptr );
if ( sequential )
{
int remainingChunks = chunk->count - msgIdx;
if ( remainingChunks >= requiredChunks )
{
m_LastFreeIndex = msgIdx + requiredChunks;
m_LastFreeChunk = chunkIdx;
if ( index )
{
Assert( msgIdx < 0x0000ffff );
Assert( chunkIdx < 0x00007fff );
*index = ( chunkIdx << 16 ) | msgIdx;
}
return &chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
}
}
else
{
char *ptr = &chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
Assert( *(int*)ptr >= 0 && *(int*)ptr < ( chunk->count - msgIdx ) * MEM_CACHE_CHUNKSIZE );
if ( *(int*)ptr == 0 )
{
if ( ++matchedChunks == requiredChunks )
{
msgIdx = msgIdx - matchedChunks + 1;
Assert( !index );
ptr = &chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
*(int*)ptr = size;
return ptr + sizeof(int);
}
}
else
{
matchedChunks = 0;
}
msgIdx += ( *(int*)ptr + sizeof(int) - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
Assert( msgIdx < 0x0000ffff );
if ( msgIdx < chunk->count )
continue;
matchedChunks = 0;
}
msgIdx = 0;
if ( ++chunkIdx >= m_MemChunkCount )
{
int oldcount = m_MemChunkCount;
m_MemChunkCount <<= 1;
m_Memory = (chunk_t*)sqdbg_realloc( m_Memory,
oldcount * sizeof(chunk_t),
m_MemChunkCount * sizeof(chunk_t) );
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
memset( (char*)m_Memory + oldcount * sizeof(chunk_t),
0,
(m_MemChunkCount - oldcount) * sizeof(chunk_t) );
}
chunk = &m_Memory[ chunkIdx ];
if ( chunk->count == 0 )
{
Assert( chunk->ptr == NULL );
chunk->count = ( requiredChunks + ( MEM_CACHE_CHUNKS_ALIGN - 1 ) ) & ~( MEM_CACHE_CHUNKS_ALIGN - 1 );
chunk->ptr = (char*)sqdbg_malloc( chunk->count * MEM_CACHE_CHUNKSIZE );
AssertOOM( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
memset( chunk->ptr, 0, chunk->count * MEM_CACHE_CHUNKSIZE );
}
Assert( chunkIdx < 0x00007fff );
}
}
void Free( void *ptr )
{
Assert( m_Memory );
Assert( ptr );
*(char**)&ptr -= sizeof(int);
#ifdef _DEBUG
bool found = false;
for ( int chunkIdx = 0; chunkIdx < m_MemChunkCount; chunkIdx++ )
{
chunk_t *chunk = &m_Memory[ chunkIdx ];
if ( chunk->count && ptr >= chunk->ptr && ptr < chunk->ptr + chunk->count * MEM_CACHE_CHUNKSIZE )
{
Assert( *(int*)ptr >= 0 );
Assert( (char*)ptr + sizeof(int) + *(int*)ptr <= chunk->ptr + chunk->count * MEM_CACHE_CHUNKSIZE );
found = true;
break;
}
}
Assert( found );
(*(unsigned char**)&ptr)[ *(int*)ptr + sizeof(int) - 1 ] = 0xdd;
#endif
memset( (char*)ptr, 0, *(int*)ptr + sizeof(int) );
}
void Free()
{
if ( !m_Memory )
return;
for ( int chunkIdx = 0; chunkIdx < m_MemChunkCount; chunkIdx++ )
{
chunk_t *chunk = &m_Memory[ chunkIdx ];
if ( chunk->count )
{
sqdbg_free( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
}
}
sqdbg_free( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
m_Memory = NULL;
m_MemChunkCount = 4;
m_LastFreeChunk = 0;
m_LastFreeIndex = 0;
}
void ReleaseShrink()
{
if ( !m_Memory )
return;
for ( int chunkIdx = 4; chunkIdx < m_MemChunkCount; chunkIdx++ )
{
chunk_t *chunk = &m_Memory[ chunkIdx ];
if ( chunk->count )
{
sqdbg_free( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
chunk->count = 0;
chunk->ptr = NULL;
}
}
#ifdef _DEBUG
for ( int chunkIdx = 0; chunkIdx < 4; chunkIdx++ )
{
chunk_t *chunk = &m_Memory[ chunkIdx ];
if ( chunk->count )
{
memset( chunk->ptr, 0xdd, chunk->count * MEM_CACHE_CHUNKSIZE );
}
}
#endif
if ( m_MemChunkCount > 8 )
{
int oldcount = m_MemChunkCount;
m_MemChunkCount = 8;
m_Memory = (chunk_t*)sqdbg_realloc( m_Memory,
oldcount * sizeof(chunk_t),
m_MemChunkCount * sizeof(chunk_t) );
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
}
m_LastFreeChunk = 0;
m_LastFreeIndex = 0;
}
void Release()
{
if ( !m_Memory || ( !m_LastFreeChunk && !m_LastFreeIndex ) )
return;
#ifdef _DEBUG
for ( int chunkIdx = 0; chunkIdx < m_MemChunkCount; chunkIdx++ )
{
chunk_t *chunk = &m_Memory[ chunkIdx ];
if ( chunk->count )
{
memset( chunk->ptr, 0xdd, chunk->count * MEM_CACHE_CHUNKSIZE );
}
}
#endif
m_LastFreeChunk = 0;
m_LastFreeIndex = 0;
}
};
template < typename T, bool bExternalMem = false, class CAllocator = CMemory >
class vector
{
public:
typedef unsigned int I;
CAllocator _base;
I _size;
vector() : _base(), _size(0)
{
Assert( !bExternalMem );
}
vector( CAllocator &a ) : _base(a), _size(0)
{
Assert( bExternalMem );
}
vector( I count ) : _base(), _size(0)
{
Assert( !bExternalMem );
_base.Alloc( count * sizeof(T) );
}
vector( const vector< T > &src ) : _base()
{
Assert( !bExternalMem );
_base.Alloc( src._base.Size() );
_size = src._size;
for ( I i = 0; i < _size; i++ )
new( &_base[ i * sizeof(T) ] ) T( (T&)src._base[ i * sizeof(T) ] );
}
~vector()
{
Assert( (unsigned int)_size <= _base.Size() );
for ( I i = 0; i < _size; i++ )
((T&)(_base[ i * sizeof(T) ])).~T();
if ( !bExternalMem )
_base.Free();
}
T &operator[]( I i ) const
{
Assert( _size > 0 );
Assert( i >= 0 && i < _size );
Assert( _size * sizeof(T) <= _base.Size() );
return (T&)_base[ i * sizeof(T) ];
}
T *base()
{
return _base.Base();
}
I size() const
{
return _size;
}
I capacity() const
{
return _base.Size() / sizeof(T);
}
T &top() const
{
Assert( _size > 0 );
return (T&)_base[ ( _size - 1 ) * sizeof(T) ];
}
void pop()
{
Assert( _size > 0 );
((T&)_base[ --_size * sizeof(T) ]).~T();
}
T &append()
{
_base.Ensure( ++_size * sizeof(T) );
Assert( _size * sizeof(T) <= _base.Size() );
return *( new( &_base[ ( _size - 1 ) * sizeof(T) ] ) T() );
}
void append( const T &src )
{
_base.Ensure( ++_size * sizeof(T) );
Assert( _size * sizeof(T) <= _base.Size() );
new( &_base[ ( _size - 1 ) * sizeof(T) ] ) T( src );
}
T &insert( I i )
{
Assert( i >= 0 && i <= _size );
_base.Ensure( ++_size * sizeof(T) );
Assert( _size * sizeof(T) <= _base.Size() );
if ( i != _size - 1 )
{
memmove( &_base[ ( i + 1 ) * sizeof(T) ],
&_base[ i * sizeof(T) ],
( _size - ( i + 1 ) ) * sizeof(T) );
}
return *( new( &_base[ i * sizeof(T) ] ) T() );
}
void remove( I i )
{
Assert( _size > 0 );
Assert( i >= 0 && i < _size );
((T&)_base[ i * sizeof(T) ]).~T();
if ( i != _size - 1 )
{
memmove( &_base[ i * sizeof(T) ],
&_base[ ( i + 1 ) * sizeof(T) ],
( _size - ( i + 1 ) ) * sizeof(T) );
}
_size--;
}
void clear()
{
for ( I i = 0; i < _size; i++ )
((T&)_base[ i * sizeof(T) ]).~T();
_size = 0;
}
void sort( int (*fn)(const T *, const T *) )
{
Assert( _size * sizeof(T) <= _base.Size() );
if ( _size > 1 )
{
qsort( _base.Base(), _size, sizeof(T), (int (*)(const void *, const void *))fn );
}
}
void reserve( I count )
{
Assert( (unsigned int)_size <= _base.Size() );
if ( count == 0 )
count = 4;
if ( (unsigned int)count == _base.Size() )
return;
for ( I i = count; i < _size; i++ )
((T&)_base[ i * sizeof(T) ]).~T();
_base.Alloc( count * sizeof(T) );
}
void purge()
{
Assert( _size * sizeof(T) <= _base.Size() );
for ( I i = 0; i < _size; i++ )
((T&)_base[ i * sizeof(T) ]).~T();
_base.Free();
_size = 0;
}
};
class CBuffer
{
public:
CMemory _base;
int _size;
int _offset;
char *base()
{
return _base.Base() + _offset;
}
int size() const
{
return _size;
}
int capacity() const
{
return _base.Size();
}
void reserve( int count )
{
Assert( (unsigned int)_size <= _base.Size() );
if ( (unsigned int)count == _base.Size() )
return;
_base.Alloc( count );
}
void purge()
{
_base.Free();
_size = 0;
_offset = 0;
}
};
class CBufTmpCache
{
public:
CBuffer *buffer;
int size;
CBufTmpCache( CBuffer *b ) :
buffer(b),
size(buffer->_size)
{
buffer->_offset += buffer->_size;
buffer->_size = 0;
}
~CBufTmpCache()
{
buffer->_offset -= size;
buffer->_size = size;
}
};
#endif // SQDBG_VEC_H

View File

@ -17,6 +17,7 @@
IScriptVM* makeSquirrelVM();
int vscript_token = 0;
int vscript_debugger_port = 0;
class CScriptManager : public CTier1AppSystem<IScriptManager>
{
@ -79,4 +80,4 @@ public:
}
};
EXPOSE_SINGLE_INTERFACE(CScriptManager, IScriptManager, VSCRIPT_INTERFACE_VERSION);
EXPOSE_SINGLE_INTERFACE(CScriptManager, IScriptManager, VSCRIPT_INTERFACE_VERSION);

View File

@ -12,7 +12,7 @@ $Configuration
{
$Compiler
{
$AdditionalIncludeDirectories "$BASE;.\squirrel\include"
$AdditionalIncludeDirectories "$BASE;.\squirrel\include;.\squirrel\squirrel;.\squirrel\sqstdlib;.\sqdbg\include"
$PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT]
}
}
@ -65,5 +65,21 @@ $Project "VScript"
}
}
}
$Folder "sqdbg"
{
$File ".\sqdbg\sqdbg\server.cpp"
{
$Configuration
{
$Compiler
{
$PreprocessorDefinitions "$BASE;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS"
$AdditionalOptions "$BASE /wd4127 /wd4146 /wd4201 /wd4244 /wd4267 /wd4456 /wd4706"
$TreatWarningsAsErrors "No"
}
}
}
}
}
}

View File

@ -33,6 +33,8 @@
#include "squirrel/squirrel/squserdata.h"
#include "squirrel/squirrel/sqclosure.h"
#include "sqdbg.h"
#include "tier1/utlbuffer.h"
#include "tier1/mapbase_con_groups.h"
#include "tier1/convar.h"
@ -113,7 +115,7 @@ public:
virtual bool Init() override;
virtual void Shutdown() override;
virtual bool ConnectDebugger() override;
virtual bool ConnectDebugger( int port = 0 ) override;
virtual void DisconnectDebugger() override;
virtual ScriptLanguage_t GetLanguage() override;
@ -298,6 +300,7 @@ public:
HSQOBJECT lastError_;
HSQOBJECT vectorClass_;
HSQOBJECT regexpClass_;
HSQDEBUGSERVER debugger_ = nullptr;
};
static char TYPETAG_VECTOR[] = "VectorTypeTag";
@ -2096,15 +2099,37 @@ void SquirrelVM::Shutdown()
}
}
bool SquirrelVM::ConnectDebugger()
bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing );
bool SquirrelVM::ConnectDebugger( int port )
{
// TODO: Debugger support
return false;
if ( !debugger_ )
{
debugger_ = sqdbg_attach_debugger( vm_ );
if ( sqdbg_listen_socket( debugger_, port ) != 0 )
{
sqdbg_destroy_debugger( vm_ );
debugger_ = nullptr;
return false;
}
}
else
{
sqdbg_frame( debugger_ );
}
VScriptRunScript( "sqdbg_definitions.nut", NULL, false );
return true;
}
void SquirrelVM::DisconnectDebugger()
{
// TODO: Debugger support
if ( debugger_ )
{
sqdbg_destroy_debugger( vm_ );
debugger_ = nullptr;
}
}
ScriptLanguage_t SquirrelVM::GetLanguage()
@ -2124,7 +2149,10 @@ void SquirrelVM::AddSearchPath(const char* pszSearchPath)
bool SquirrelVM::Frame(float simTime)
{
// TODO: Frame support
if ( debugger_ )
{
sqdbg_frame( debugger_ );
}
return false;
}
@ -2151,12 +2179,24 @@ HSCRIPT SquirrelVM::CompileScript(const char* pszScript, const char* pszId)
{
SquirrelSafeCheck safeCheck(vm_);
Assert(vm_);
if (pszId == nullptr) pszId = "<unnamed>";
if (SQ_FAILED(sq_compilebuffer(vm_, pszScript, strlen(pszScript), pszId, SQTrue)))
bool bUnnamed = ( pszId == nullptr );
if ( bUnnamed )
{
pszId = "<unnamed>";
}
int nScriptLen = strlen(pszScript);
if (SQ_FAILED(sq_compilebuffer(vm_, pszScript, nScriptLen, pszId, SQTrue)))
{
return nullptr;
}
if ( debugger_ && !bUnnamed )
{
sqdbg_on_script_compile( debugger_, pszScript, nScriptLen, pszId, strlen(pszId) );
}
HSQOBJECT* obj = new HSQOBJECT;
sq_resetobject(obj);
sq_getstackobj(vm_, -1, obj);