5 #include "sqdbgserver.h"
9 #define scstrcpy strcpy
11 #define scstrcpy wcscpy
18 #define SQDBG_DEBUG_HOOK _SC("_sqdbg_debug_hook_")
19 #define SQDBG_ERROR_HANDLER _SC("_sqdbg_error_handler_")
21 XMLEscape g_escapes[]={
22 {_SC('<'),_SC("<")},{'>',_SC(">")},{_SC('&'),_SC("&")},{_SC('\''),_SC("'")},{_SC('\"'),_SC(""")},{_SC('\n'),_SC(""n")},{_SC('\r'),_SC(""r")},{0, NULL}
25 const SQChar *IntToString(int n)
27 static SQChar temp[256];
28 scsprintf(temp,_SC("%d"),n);
32 SQInteger debug_hook(HSQUIRRELVM v);
33 SQInteger error_handler(HSQUIRRELVM v);
35 SQInteger beginelement(HSQUIRRELVM v)
39 sq_getuserpointer(v,-1,&up);
40 SQDbgServer *self = (SQDbgServer*)up;
41 sq_getuserpointer(v,-1,&up);
42 sq_getstring(v,2,&name);
43 self->BeginElement(name);
47 SQInteger endelement(HSQUIRRELVM v)
51 sq_getuserpointer(v,-1,&up);
52 SQDbgServer *self = (SQDbgServer*)up;
53 sq_getuserpointer(v,-1,&up);
54 sq_getstring(v,2,&name);
55 self->EndElement(name);
59 SQInteger attribute(HSQUIRRELVM v)
62 const SQChar *name,*value;
63 sq_getuserpointer(v,-1,&up);
64 SQDbgServer *self = (SQDbgServer*)up;
65 sq_getuserpointer(v,-1,&up);
66 sq_getstring(v,2,&name);
67 sq_getstring(v,3,&value);
68 self->Attribute(name,value);
72 const SQChar *EscapeXMLString(HSQUIRRELVM v,const SQChar *s)
75 SQChar *temp=sq_getscratchpad(v,((int)scstrlen(s)*6) + sizeof(SQChar));
80 while(g_escapes[i].esc!=NULL){
81 if(*s==g_escapes[i].c){
82 scstrcpy(dest,g_escapes[i].esc);
83 dest+=scstrlen(g_escapes[i].esc);
89 if(!escaped){*dest=*s;*dest++;}
96 SQDbgServer::SQDbgServer(HSQUIRRELVM v)
102 _state = eDBG_Running;
103 _accept = INVALID_SOCKET;
104 _endpoint = INVALID_SOCKET;
106 sq_resetobject(&_debugroot);
109 SQDbgServer::~SQDbgServer()
111 sq_release(_v,&_debugroot);
112 if(_accept != INVALID_SOCKET)
113 sqdbg_closesocket(_accept);
114 if(_endpoint != INVALID_SOCKET)
115 sqdbg_closesocket(_endpoint);
118 bool SQDbgServer::Init()
120 //creates an environment table for the debugger
123 sq_getstackobj(_v,-1,&_debugroot);
124 sq_addref(_v,&_debugroot);
126 //creates a emptyslot to store the watches
127 sq_pushstring(_v,_SC("watches"),-1);
129 sq_createslot(_v,-3);
131 sq_pushstring(_v,_SC("beginelement"),-1);
132 sq_pushuserpointer(_v,this);
133 sq_newclosure(_v,beginelement,1);
134 sq_setparamscheck(_v,2,_SC(".s"));
135 sq_createslot(_v,-3);
137 sq_pushstring(_v,_SC("endelement"),-1);
138 sq_pushuserpointer(_v,this);
139 sq_newclosure(_v,endelement,1);
140 sq_setparamscheck(_v,2,_SC(".s"));
141 sq_createslot(_v,-3);
143 sq_pushstring(_v,_SC("attribute"),-1);
144 sq_pushuserpointer(_v,this);
145 sq_newclosure(_v,attribute,1);
146 sq_setparamscheck(_v,3,_SC(".ss"));
147 sq_createslot(_v,-3);
151 //stores debug hook and error handler in the registry
152 sq_pushregistrytable(_v);
154 sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
155 sq_pushuserpointer(_v,this);
156 sq_newclosure(_v,debug_hook,1);
157 sq_createslot(_v,-3);
159 sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
160 sq_pushuserpointer(_v,this);
161 sq_newclosure(_v,error_handler,1);
162 sq_createslot(_v,-3);
167 //sets the error handlers
172 bool SQDbgServer::ReadMsg()
177 void SQDbgServer::BusyWait()
183 void SQDbgServer::SendChunk(const SQChar *chunk)
188 buf_len=(int)scstrlen(chunk)+1;
189 buf=(char *)sq_getscratchpad(_v,(buf_len)*3);
190 wcstombs((char *)buf,chunk,buf_len);
192 buf_len=(int)scstrlen(chunk);
195 send(_endpoint,(const char*)buf,(int)strlen((const char *)buf),0);
199 void SQDbgServer::Terminated()
201 BeginElement(_SC("terminated"));
202 EndElement(_SC("terminated"));
206 void SQDbgServer::Hook(int type,int line,const SQChar *src,const SQChar *func)
210 if(type==_SC('l') && _breakpoints.size()) {
211 BreakPointSetItor itr = _breakpoints.find(BreakPoint(line,src));
212 if(itr != _breakpoints.end()) {
213 Break(line,src,_SC("breakpoint"));
223 if(_nestedcalls==0) {
224 Break(line,src,_SC("step"));
245 Break(line,src,_SC("step"));
251 case eDBG_StepReturn:
261 _state=eDBG_StepOver;
275 #define MSG_ID(x,y) ((y<<8)|x)
277 //rb Remove Breakpoint
279 void SQDbgServer::ParseMsg(const char *msg)
282 switch(*((unsigned short *)msg)){
283 case MSG_ID('a','b'): {
285 if(ParseBreakpoint(msg+3,bp)){
287 scprintf(_SC("added bp %d %s\n"),bp._line,bp._src.c_str());
290 scprintf(_SC("error parsing add breakpoint"));
293 case MSG_ID('r','b'): {
295 if(ParseBreakpoint(msg+3,bp)){
296 RemoveBreakpoint(bp);
297 scprintf(_SC("removed bp %d %s\n"),bp._line,bp._src.c_str());
299 scprintf(_SC("error parsing remove breakpoint"));
302 case MSG_ID('g','o'):
303 if(_state!=eDBG_Running){
306 BeginElement(_SC("resumed"));
307 EndElement(_SC("resumed"));
309 // Send(_SC("<resumed/>\r\n"));
310 scprintf(_SC("go (execution resumed)\n"));
313 case MSG_ID('s','p'):
314 if(_state!=eDBG_Suspended){
315 _state=eDBG_Suspended;
316 scprintf(_SC("suspend\n"));
319 case MSG_ID('s','o'):
320 if(_state==eDBG_Suspended){
321 _state=eDBG_StepOver;
324 case MSG_ID('s','i'):
325 if(_state==eDBG_Suspended){
326 _state=eDBG_StepInto;
327 scprintf(_SC("step into\n"));
330 case MSG_ID('s','r'):
331 if(_state==eDBG_Suspended){
332 _state=eDBG_StepReturn;
333 scprintf(_SC("step return\n"));
336 case MSG_ID('d','i'):
337 if(_state!=eDBG_Disabled){
338 _state=eDBG_Disabled;
339 scprintf(_SC("disabled\n"));
342 case MSG_ID('a','w'): {
344 if(ParseWatch(msg+3,w))
347 scprintf(_SC("added watch %d %s\n"),w._id,w._exp.c_str());
350 scprintf(_SC("error parsing add watch"));
353 case MSG_ID('r','w'): {
355 if(ParseRemoveWatch(msg+3,id))
358 scprintf(_SC("added watch %d\n"),id);
361 scprintf(_SC("error parsing remove watch"));
364 case MSG_ID('t','r'):
365 scprintf(_SC("terminate from user\n"));
367 case MSG_ID('r','d'):
368 scprintf(_SC("ready\n"));
372 scprintf(_SC("unknown packet"));
378 see copyright notice in sqrdbg.h
380 bool SQDbgServer::ParseBreakpoint(const char *msg,BreakPoint &out)
382 static char stemp[MAX_BP_PATH];
384 out._line=strtoul(msg,&ep,16);
385 if(ep==msg || (*ep)!=':')return false;
389 while((*ep)!='\n' && (*ep)!='\0')
398 int len=(int)strlen(stemp);
399 SQChar *p=sq_getscratchpad(_v,(SQInteger)(mbstowcs(NULL,stemp,len)+2)*sizeof(SQChar));
400 size_t destlen=mbstowcs(p,stemp,len);
401 p[destlen]=_SC('\0');
409 bool SQDbgServer::ParseWatch(const char *msg,Watch &out)
412 out._id=strtoul(msg,&ep,16);
413 if(ep==msg || (*ep)!=':')return false;
415 //char *dest=out._src;
417 while((*ep)!='\n' && (*ep)!='\0')
419 out._exp.append(1,*ep);
425 bool SQDbgServer::ParseRemoveWatch(const char *msg,int &id)
428 id=strtoul(msg,&ep,16);
429 if(ep==msg)return false;
434 void SQDbgServer::BreakExecution()
436 _state=eDBG_Suspended;
437 while(_state==eDBG_Suspended){
438 if(SQ_FAILED(sq_rdbg_update(this)))
445 void SQDbgServer::AddBreakpoint(BreakPoint &bp)
447 _breakpoints.insert(bp);
449 BeginElement(_SC("addbreakpoint"));
450 Attribute(_SC("line"),IntToString(bp._line));
451 Attribute(_SC("src"),bp._src.c_str());
452 EndElement(_SC("addbreakpoint"));
456 void SQDbgServer::AddWatch(Watch &w)
461 void SQDbgServer::RemoveWatch(int id)
463 WatchSetItor itor=_watches.find(Watch(id,_SC("")));
464 if(itor==_watches.end()){
466 BeginElement(_SC("error"));
467 Attribute(_SC("desc"),_SC("the watch does not exists"));
468 EndElement(_SC("error"));
472 _watches.erase(itor);
473 scprintf(_SC("removed watch %d\n"),id);
477 void SQDbgServer::RemoveBreakpoint(BreakPoint &bp)
479 BreakPointSetItor itor=_breakpoints.find(bp);
480 if(itor==_breakpoints.end()){
482 BeginElement(_SC("break"));
483 Attribute(_SC("desc"),_SC("the breakpoint doesn't exists"));
484 EndElement(_SC("break"));
489 BeginElement(_SC("removebreakpoint"));
490 Attribute(_SC("line"),IntToString(bp._line));
491 Attribute(_SC("src"),bp._src.c_str());
492 EndElement(_SC("removebreakpoint"));
494 _breakpoints.erase(itor);
498 void SQDbgServer::Break(int line,const SQChar *src,const SQChar *type,const SQChar *error)
502 BeginElement(_SC("break"));
503 Attribute(_SC("line"),IntToString(line));
504 Attribute(_SC("src"),src);
505 Attribute(_SC("type"),type);
507 EndElement(_SC("break"));
511 BeginElement(_SC("break"));
512 Attribute(_SC("line"),IntToString(line));
513 Attribute(_SC("src"),src);
514 Attribute(_SC("type"),type);
515 Attribute(_SC("error"),error);
517 EndElement(_SC("break"));
522 void SQDbgServer::SerializeState()
527 sq_seterrorhandler(_v);
529 sq_pushobject(_v,_serializefunc);
530 sq_pushobject(_v,_debugroot);
531 sq_pushstring(_v,_SC("watches"),-1);
533 for(WatchSetItor i=_watches.begin(); i!=_watches.end(); ++i)
535 sq_pushinteger(_v,i->_id);
536 sq_pushstring(_v,i->_exp.c_str(),(int)i->_exp.length());
537 sq_createslot(_v,-3);
540 if(SQ_SUCCEEDED(sq_call(_v,1,SQTrue,SQTrue))){
541 if(SQ_SUCCEEDED(sqstd_getblob(_v,-1,(SQUserPointer*)&sz)))
550 void SQDbgServer::SetErrorHandlers()
552 sq_pushregistrytable(_v);
553 sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
556 sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
558 sq_seterrorhandler(_v);
562 void SQDbgServer::BeginElement(const SQChar *name)
565 XMLElementState *self = &xmlstate[_xmlcurrentement];
566 scstrcpy(self->name,name);
567 self->haschildren = false;
568 if(_xmlcurrentement > 0) {
569 XMLElementState *parent = &xmlstate[_xmlcurrentement-1];
570 if(!parent->haschildren) {
571 SendChunk(_SC(">")); // closes the parent tag
572 parent->haschildren = true;
575 _scratchstring.resize(2+scstrlen(name));
576 scsprintf(&_scratchstring[0],_SC("<%s"),name);
577 SendChunk(&_scratchstring[0]);
580 void SQDbgServer::Attribute(const SQChar *name,const SQChar *value)
582 XMLElementState *self = &xmlstate[_xmlcurrentement];
583 assert(!self->haschildren); //cannot have attributes if already has children
584 const SQChar *escval = escape_xml(value);
585 _scratchstring.resize(5+scstrlen(name)+scstrlen(escval));
586 scsprintf(&_scratchstring[0],_SC(" %s=\"%s\""),name,escval);
587 SendChunk(&_scratchstring[0]);
590 void SQDbgServer::EndElement(const SQChar *name)
592 XMLElementState *self = &xmlstate[_xmlcurrentement];
593 assert(scstrcmp(self->name,name) == 0);
594 if(self->haschildren) {
595 _scratchstring.resize(4+scstrlen(name));
596 scsprintf(&_scratchstring[0],_SC("</%s>"),name);
597 SendChunk(&_scratchstring[0]);
601 SendChunk(_SC("/>"));
606 void SQDbgServer::EndDocument()
608 SendChunk(_SC("\r\n"));
611 //this can be done much better/faster(do we need that?)
612 const SQChar *SQDbgServer::escape_xml(const SQChar *s)
614 SQChar *temp=sq_getscratchpad(_v,((int)scstrlen(s)*6) + sizeof(SQChar));
616 while(*s!=_SC('\0')){
619 while(g_escapes[i].esc!=NULL){
620 if(*s==g_escapes[i].c){
621 scstrcpy(dest,g_escapes[i].esc);
622 dest+=scstrlen(g_escapes[i].esc);
628 if(!escaped){*dest=*s;*dest++;}