1 #include <squirrel.h>
\r
6 #include <sqstdblob.h>
\r
8 #include "sqdbgserver.h"
\r
11 # define Sleep sleep
\r
12 # include <sys/types.h>
\r
13 # include <sys/socket.h>
\r
17 #define scstrcpy strcpy
\r
19 #define scstrcpy wcscpy
\r
26 #define SQDBG_DEBUG_HOOK _SC("_sqdbg_debug_hook_")
\r
27 #define SQDBG_ERROR_HANDLER _SC("_sqdbg_error_handler_")
\r
29 XMLEscape g_escapes[]={
\r
30 {_SC('<'),_SC("<")},{'>',_SC(">")},{_SC('&'),_SC("&")},{_SC('\''),_SC("'")},{_SC('\"'),_SC(""")},{_SC('\n'),_SC(""n")},{_SC('\r'),_SC(""r")},{NULL,NULL}
\r
33 const SQChar *IntToString(SQInteger n)
\r
35 static SQChar temp[256];
\r
36 scsprintf(temp,_SC("%d"),n);
\r
40 const SQChar *PtrToString(void *p)
\r
42 static SQChar temp[256];
\r
43 scsprintf(temp,_SC("%p"),p);
\r
47 SQInteger debug_hook(HSQUIRRELVM v);
\r
48 SQInteger error_handler(HSQUIRRELVM v);
\r
50 SQInteger beginelement(HSQUIRRELVM v)
\r
54 sq_getuserpointer(v,-1,&up);
\r
55 SQDbgServer *self = (SQDbgServer*)up;
\r
56 sq_getuserpointer(v,-1,&up);
\r
57 sq_getstring(v,2,&name);
\r
58 self->BeginElement(name);
\r
62 SQInteger endelement(HSQUIRRELVM v)
\r
66 sq_getuserpointer(v,-1,&up);
\r
67 SQDbgServer *self = (SQDbgServer*)up;
\r
68 sq_getuserpointer(v,-1,&up);
\r
69 sq_getstring(v,2,&name);
\r
70 self->EndElement(name);
\r
74 SQInteger attribute(HSQUIRRELVM v)
\r
77 const SQChar *name,*value;
\r
78 sq_getuserpointer(v,-1,&up);
\r
79 SQDbgServer *self = (SQDbgServer*)up;
\r
80 sq_getuserpointer(v,-1,&up);
\r
81 sq_getstring(v,2,&name);
\r
82 sq_getstring(v,3,&value);
\r
83 self->Attribute(name,value);
\r
87 SQDbgServer::SQDbgServer(HSQUIRRELVM v)
\r
91 _autoupdate = false;
\r
93 _state = eDBG_Running;
\r
94 _accept = INVALID_SOCKET;
\r
95 _endpoint = INVALID_SOCKET;
\r
96 //_maxrecursion = 10;
\r
97 sq_resetobject(&_debugroot);
\r
100 SQDbgServer::~SQDbgServer()
\r
102 VMStateMap::iterator itr = _vmstate.begin();
\r
103 while(itr != _vmstate.end()) {
\r
104 VMState *vs = itr->second;
\r
109 sq_pushobject(_v,_debugroot);
\r
111 sq_release(_v,&_debugroot);
\r
112 if(_accept != INVALID_SOCKET)
\r
113 sqdbg_closesocket(_accept);
\r
114 if(_endpoint != INVALID_SOCKET)
\r
115 sqdbg_closesocket(_endpoint);
\r
118 bool SQDbgServer::Init()
\r
120 //creates an environment table for the debugger
\r
123 sq_getstackobj(_v,-1,&_debugroot);
\r
124 sq_addref(_v,&_debugroot);
\r
126 //creates a emptyslot to store the watches
\r
127 sq_pushstring(_v,_SC("watches"),-1);
\r
129 sq_newslot(_v,-3, SQFalse);
\r
131 sq_pushstring(_v,_SC("beginelement"),-1);
\r
132 sq_pushuserpointer(_v,this);
\r
133 sq_newclosure(_v,beginelement,1);
\r
134 sq_setparamscheck(_v,2,_SC(".s"));
\r
135 sq_newslot(_v,-3, SQFalse);
\r
137 sq_pushstring(_v,_SC("endelement"),-1);
\r
138 sq_pushuserpointer(_v,this);
\r
139 sq_newclosure(_v,endelement,1);
\r
140 sq_setparamscheck(_v,2,_SC(".s"));
\r
141 sq_newslot(_v,-3, SQFalse);
\r
143 sq_pushstring(_v,_SC("attribute"),-1);
\r
144 sq_pushuserpointer(_v,this);
\r
145 sq_newclosure(_v,attribute,1);
\r
146 sq_setparamscheck(_v,3,_SC(".ss"));
\r
147 sq_newslot(_v,-3, SQFalse);
\r
151 //stores debug hook and error handler in the registry
\r
152 sq_pushregistrytable(_v);
\r
154 sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
\r
155 sq_pushuserpointer(_v,this);
\r
156 sq_newclosure(_v,debug_hook,1);
\r
157 sq_newslot(_v,-3, SQFalse);
\r
159 sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
\r
160 sq_pushuserpointer(_v,this);
\r
161 sq_newclosure(_v,error_handler,1);
\r
162 sq_newslot(_v,-3, SQFalse);
\r
167 //sets the error handlers
\r
168 SetErrorHandlers(_v);
\r
172 bool SQDbgServer::ReadMsg()
\r
177 void SQDbgServer::BusyWait()
\r
179 while( !ReadMsg() )
\r
185 void SQDbgServer::SendChunk(const SQChar *chunk)
\r
190 buf_len=(int)scstrlen(chunk)+1;
\r
191 buf=(char *)sq_getscratchpad(_v,(buf_len)*3);
\r
192 //wcstombs((char *)buf,chunk,buf_len*3);
\r
193 WideCharToMultiByte(CP_UTF8,0,chunk,-1,buf,buf_len*3,NULL,NULL);
\r
195 buf_len=(int)scstrlen(chunk);
\r
198 send(_endpoint,(const char*)buf,(int)strlen((const char *)buf),0);
\r
202 void SQDbgServer::Terminated()
\r
204 BeginElement(_SC("terminated"));
\r
205 EndElement(_SC("terminated"));
\r
209 VMState *SQDbgServer::GetVMState(HSQUIRRELVM v)
\r
211 VMState *ret = NULL;
\r
212 VMStateMap::iterator itr = _vmstate.find(v);
\r
213 if(itr == _vmstate.end()) {
\r
214 ret = new VMState();
\r
215 _vmstate.insert(VMStateMap::value_type(v,ret));
\r
223 void SQDbgServer::Hook(HSQUIRRELVM v,SQInteger type,SQInteger line,const SQChar *src,const SQChar *func)
\r
226 VMState *vs = GetVMState(v);
\r
229 if(type==_SC('l') && _breakpoints.size()) {
\r
230 BreakPointSetItor itr = _breakpoints.find(BreakPoint(line,src));
\r
231 if(itr != _breakpoints.end()) {
\r
232 Break(v,line,src,_SC("breakpoint"));
\r
237 case eDBG_Suspended:
\r
238 vs->_nestedcalls=0;
\r
239 case eDBG_StepOver:
\r
242 if(vs->_nestedcalls==0) {
\r
243 Break(v,line,src,_SC("step"));
\r
248 vs->_nestedcalls++;
\r
251 if(vs->_nestedcalls==0){
\r
252 vs->_nestedcalls=0;
\r
255 vs->_nestedcalls--;
\r
260 case eDBG_StepInto:
\r
263 vs->_nestedcalls=0;
\r
264 Break(v,line,src,_SC("step"));
\r
270 case eDBG_StepReturn:
\r
275 vs->_nestedcalls++;
\r
278 if(vs->_nestedcalls==0){
\r
279 vs->_nestedcalls=0;
\r
280 _state=eDBG_StepOver;
\r
282 vs->_nestedcalls--;
\r
288 case eDBG_Disabled:
\r
294 #define MSG_ID(x,y) ((y<<8)|x)
\r
295 //ab Add Breakpoint
\r
296 //rb Remove Breakpoint
\r
298 void SQDbgServer::ParseMsg(const char *msg)
\r
301 switch(*((unsigned short *)msg)){
\r
302 case MSG_ID('a','b'): {
\r
304 if(ParseBreakpoint(msg+3,bp)){
\r
306 scprintf(_SC("added bp %d %s\n"),bp._line,bp._src.c_str());
\r
309 scprintf(_SC("error parsing add breakpoint"));
\r
312 case MSG_ID('r','b'): {
\r
314 if(ParseBreakpoint(msg+3,bp)){
\r
315 RemoveBreakpoint(bp);
\r
316 scprintf(_SC("removed bp %d %s\n"),bp._line,bp._src.c_str());
\r
318 scprintf(_SC("error parsing remove breakpoint"));
\r
321 case MSG_ID('g','o'):
\r
322 if(_state!=eDBG_Running){
\r
323 _state=eDBG_Running;
\r
325 BeginElement(_SC("resumed"));
\r
326 EndElement(_SC("resumed"));
\r
328 // Send(_SC("<resumed/>\r\n"));
\r
329 scprintf(_SC("go (execution resumed)\n"));
\r
332 case MSG_ID('s','p'):
\r
333 if(_state!=eDBG_Suspended){
\r
334 _state=eDBG_Suspended;
\r
335 scprintf(_SC("suspend\n"));
\r
338 case MSG_ID('s','o'):
\r
339 if(_state==eDBG_Suspended){
\r
340 _state=eDBG_StepOver;
\r
343 case MSG_ID('s','i'):
\r
344 if(_state==eDBG_Suspended){
\r
345 _state=eDBG_StepInto;
\r
346 scprintf(_SC("step into\n"));
\r
349 case MSG_ID('s','r'):
\r
350 if(_state==eDBG_Suspended){
\r
351 _state=eDBG_StepReturn;
\r
352 scprintf(_SC("step return\n"));
\r
355 case MSG_ID('d','i'):
\r
356 if(_state!=eDBG_Disabled){
\r
357 _state=eDBG_Disabled;
\r
358 scprintf(_SC("disabled\n"));
\r
361 case MSG_ID('a','w'): {
\r
363 if(ParseWatch(msg+3,w))
\r
366 scprintf(_SC("added watch %d %s\n"),w._id,w._exp.c_str());
\r
367 /*if(_state == eDBG_Suspended) {
\r
368 Break(_line,_src.c_str(),_break_type.c_str());
\r
372 scprintf(_SC("error parsing add watch"));
\r
375 case MSG_ID('r','w'): {
\r
377 if(ParseRemoveWatch(msg+3,id))
\r
380 scprintf(_SC("added watch %d\n"),id);
\r
383 scprintf(_SC("error parsing remove watch"));
\r
386 case MSG_ID('t','r'):
\r
387 scprintf(_SC("terminate from user\n"));
\r
389 case MSG_ID('r','d'):
\r
390 scprintf(_SC("ready\n"));
\r
394 scprintf(_SC("unknown packet"));
\r
399 bool SQDbgServer::ParseBreakpoint(const char *msg,BreakPoint &out)
\r
401 static char stemp[MAX_BP_PATH];
\r
402 static SQChar desttemp[MAX_BP_PATH];
\r
404 out._line=strtoul(msg,&ep,16);
\r
405 if(ep==msg || (*ep)!=':')return false;
\r
409 while((*ep)!='\n' && (*ep)!='\0')
\r
411 *dest=tolower(*ep);
\r
418 int len=(int)strlen(stemp);
\r
419 SQChar *p = desttemp;
\r
420 size_t destlen = mbstowcs(p,stemp,len);
\r
421 p[destlen]=_SC('\0');
\r
429 bool SQDbgServer::ParseWatch(const char *msg,Watch &out)
\r
432 out._id=strtoul(msg,&ep,16);
\r
433 if(ep==msg || (*ep)!=':')return false;
\r
435 //char *dest=out._src;
\r
437 while((*ep)!='\n' && (*ep)!='\0')
\r
439 out._exp.append(1,*ep);
\r
445 bool SQDbgServer::ParseRemoveWatch(const char *msg,SQInteger &id)
\r
448 id=strtoul(msg,&ep,16);
\r
449 if(ep==msg)return false;
\r
454 void SQDbgServer::BreakExecution()
\r
456 _state=eDBG_Suspended;
\r
457 while(_state==eDBG_Suspended){
\r
458 if(SQ_FAILED(sq_rdbg_update(this)))
\r
465 void SQDbgServer::AddBreakpoint(BreakPoint &bp)
\r
467 _breakpoints.insert(bp);
\r
469 BeginElement(_SC("addbreakpoint"));
\r
470 Attribute(_SC("line"),IntToString(bp._line));
\r
471 Attribute(_SC("src"),bp._src.c_str());
\r
472 EndElement(_SC("addbreakpoint"));
\r
476 void SQDbgServer::AddWatch(Watch &w)
\r
478 _watches.insert(w);
\r
481 void SQDbgServer::RemoveWatch(SQInteger id)
\r
483 WatchSetItor itor=_watches.find(Watch(id,_SC("")));
\r
484 if(itor==_watches.end()){
\r
486 BeginElement(_SC("error"));
\r
487 Attribute(_SC("desc"),_SC("the watch does not exists"));
\r
488 EndElement(_SC("error"));
\r
492 _watches.erase(itor);
\r
493 scprintf(_SC("removed watch %d\n"),id);
\r
497 void SQDbgServer::RemoveBreakpoint(BreakPoint &bp)
\r
499 BreakPointSetItor itor=_breakpoints.find(bp);
\r
500 if(itor==_breakpoints.end()){
\r
502 BeginElement(_SC("break"));
\r
503 Attribute(_SC("desc"),_SC("the breakpoint doesn't exists"));
\r
504 EndElement(_SC("break"));
\r
509 BeginElement(_SC("removebreakpoint"));
\r
510 Attribute(_SC("line"),IntToString(bp._line));
\r
511 Attribute(_SC("src"),bp._src.c_str());
\r
512 EndElement(_SC("removebreakpoint"));
\r
514 _breakpoints.erase(itor);
\r
518 void SQDbgServer::Break(HSQUIRRELVM v,SQInteger line,const SQChar *src,const SQChar *type,const SQChar *error)
\r
525 BeginElement(_SC("break"));
\r
526 Attribute(_SC("thread"),PtrToString(v));
\r
527 Attribute(_SC("line"),IntToString(line));
\r
528 Attribute(_SC("src"),src);
\r
529 Attribute(_SC("type"),type);
\r
531 EndElement(_SC("break"));
\r
535 BeginElement(_SC("break"));
\r
536 Attribute(_SC("thread"),PtrToString(v));
\r
537 Attribute(_SC("line"),IntToString(line));
\r
538 Attribute(_SC("src"),src);
\r
539 Attribute(_SC("type"),type);
\r
540 Attribute(_SC("error"),error);
\r
542 EndElement(_SC("break"));
\r
547 void SQDbgServer::SerializeState(HSQUIRRELVM v)
\r
550 sq_setdebughook(v);
\r
552 sq_seterrorhandler(v);
\r
553 sq_pushobject(v,_serializefunc);
\r
554 sq_pushobject(v,_debugroot);
\r
555 sq_pushstring(v,_SC("watches"),-1);
\r
557 for(WatchSetItor i=_watches.begin(); i!=_watches.end(); ++i)
\r
559 sq_pushinteger(v,i->_id);
\r
560 sq_pushstring(v,i->_exp.c_str(),(SQInteger)i->_exp.length());
\r
561 sq_createslot(v,-3);
\r
564 if(SQ_SUCCEEDED(sq_call(v,1,SQTrue,SQFalse))){
\r
565 //if(SQ_SUCCEEDED(sqstd_getblob(v,-1,(SQUserPointer*)&sz)))
\r
570 SetErrorHandlers(v);
\r
574 void SQDbgServer::SetErrorHandlers(HSQUIRRELVM v)
\r
576 sq_pushregistrytable(v);
\r
577 sq_pushstring(v,SQDBG_DEBUG_HOOK,-1);
\r
579 sq_setdebughook(v);
\r
580 sq_pushstring(v,SQDBG_ERROR_HANDLER,-1);
\r
582 sq_seterrorhandler(v);
\r
586 void SQDbgServer::BeginDocument()
\r
588 _xmlcurrentement = -1;
\r
589 SendChunk(_SC("<?xml version='1.0' encoding='utf-8'?>"));
\r
592 void SQDbgServer::BeginElement(const SQChar *name)
\r
594 _xmlcurrentement++;
\r
595 XMLElementState *self = &xmlstate[_xmlcurrentement];
\r
596 scstrcpy(self->name,name);
\r
597 self->haschildren = false;
\r
598 if(_xmlcurrentement > 0) {
\r
599 XMLElementState *parent = &xmlstate[_xmlcurrentement-1];
\r
600 if(!parent->haschildren) {
\r
601 SendChunk(_SC(">")); // closes the parent tag
\r
602 parent->haschildren = true;
\r
605 _scratchstring.resize(2+scstrlen(name));
\r
606 scsprintf(&_scratchstring[0],_SC("<%s"),name);
\r
607 SendChunk(&_scratchstring[0]);
\r
610 void SQDbgServer::Attribute(const SQChar *name,const SQChar *value)
\r
612 XMLElementState *self = &xmlstate[_xmlcurrentement];
\r
613 assert(!self->haschildren); //cannot have attributes if already has children
\r
614 const SQChar *escval = escape_xml(value);
\r
615 _scratchstring.resize(10+scstrlen(name)+scstrlen(escval));
\r
616 scsprintf(&_scratchstring[0],_SC(" %s=\"%s\""),name,escval);
\r
617 SendChunk(&_scratchstring[0]);
\r
620 void SQDbgServer::EndElement(const SQChar *name)
\r
622 XMLElementState *self = &xmlstate[_xmlcurrentement];
\r
623 assert(scstrcmp(self->name,name) == 0);
\r
624 if(self->haschildren) {
\r
625 _scratchstring.resize(10+scstrlen(name));
\r
626 scsprintf(&_scratchstring[0],_SC("</%s>"),name);
\r
627 SendChunk(&_scratchstring[0]);
\r
631 SendChunk(_SC("/>"));
\r
633 _xmlcurrentement--;
\r
636 void SQDbgServer::EndDocument()
\r
638 SendChunk(_SC("\r\n"));
\r
641 //this can be done much better/faster(do we need that?)
\r
642 const SQChar *SQDbgServer::escape_xml(const SQChar *s)
\r
644 SQChar *temp=sq_getscratchpad(_v,((SQInteger)scstrlen(s)*6) + sizeof(SQChar));
\r
646 while(*s!=_SC('\0')){
\r
648 const SQChar *escape = NULL;
\r
650 case _SC('<'): escape = _SC("<"); break;
\r
651 case _SC('>'): escape = _SC(">"); break;
\r
652 case _SC('&'): escape = _SC("&"); break;
\r
653 case _SC('\''): escape = _SC("'"); break;
\r
654 case _SC('\"'): escape = _SC("""); break;
\r
655 case _SC('\n'): escape = _SC("\\n"); break;
\r
656 case _SC('\r'): escape = _SC("\\r"); break;
\r
659 scstrcpy(dest,escape);
\r
660 dest += scstrlen(escape);
\r